Use golang v1.18 and drop vendor folder #478
19
.drone.yml
19
.drone.yml
|
@ -7,9 +7,17 @@ platform:
|
|||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: vendor
|
||||
pull: always
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
commands:
|
||||
- make vendor # use vendor folder as cache
|
||||
|
||||
- name: build
|
||||
pull: always
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
commands:
|
||||
|
@ -18,7 +26,6 @@ steps:
|
|||
- make lint
|
||||
- make fmt-check
|
||||
- make misspell-check
|
||||
- make test-vendor
|
||||
- make build
|
||||
when:
|
||||
event:
|
||||
|
@ -27,7 +34,7 @@ steps:
|
|||
- pull_request
|
||||
|
||||
- name: unit-test
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make unit-test-coverage
|
||||
settings:
|
||||
|
@ -42,7 +49,7 @@ steps:
|
|||
- pull_request
|
||||
|
||||
- name: release-test
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make test
|
||||
settings:
|
||||
|
@ -58,7 +65,7 @@ steps:
|
|||
|
||||
- name: tag-test
|
||||
pull: always
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make test
|
||||
settings:
|
||||
|
@ -70,7 +77,7 @@ steps:
|
|||
- tag
|
||||
|
||||
- name: static
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
commands:
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ tea
|
|||
dist/
|
||||
|
||||
.vscode/
|
||||
vendor/
|
||||
|
|
51
Makefile
51
Makefile
|
@ -63,24 +63,15 @@ vet:
|
|||
$(GO) vet -vettool=gitea-vet $(PACKAGES)
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
cd /tmp && $(GO) get -u github.com/mgechev/revive; \
|
||||
fi
|
||||
lint: install-lint-tools
|
||||
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell-check: install-lint-tools
|
||||
misspell -error -i unknwon,destory $(GOFILES)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell: install-lint-tools
|
||||
misspell -w -i unknwon $(GOFILES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
|
@ -105,15 +96,6 @@ unit-test-coverage:
|
|||
vendor:
|
||||
$(GO) mod tidy && $(GO) mod vendor
|
||||
|
||||
.PHONY: test-vendor
|
||||
test-vendor: vendor
|
||||
@diff=$$(git diff vendor/); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make vendor' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
||||
|
@ -141,18 +123,29 @@ release-dirs:
|
|||
|
||||
.PHONY: release-os
|
||||
release-os:
|
||||
@hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
cd /tmp && $(GO) get -u github.com/mitchellh/gox; \
|
||||
fi
|
||||
CGO_ENABLED=0 gox -verbose -cgo=false $(GOFLAGS) -osarch='!darwin/386 !darwin/arm' -os="windows linux darwin" -arch="386 amd64 arm arm64" -output="$(DIST)/release/tea-$(VERSION)-{{.OS}}-{{.Arch}}"
|
||||
|
||||
.PHONY: release-compress
|
||||
release-compress:
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
fi
|
||||
release-compress: install-release-tools
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-check
|
||||
release-check:
|
||||
release-check: install-release-tools
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
||||
|
||||
### tools
|
||||
install-release-tools:
|
||||
@hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/mitchellh/gox@latest; \
|
||||
fi
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/ulikunitz/xz/cmd/gxz@latest; \
|
||||
fi
|
||||
|
||||
install-lint-tools:
|
||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/mgechev/revive@latest; \
|
||||
fi
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
|
||||
fi
|
||||
|
|
30
vendor/code.gitea.io/gitea-vet/.changelog.yml
generated
vendored
30
vendor/code.gitea.io/gitea-vet/.changelog.yml
generated
vendored
|
@ -1,30 +0,0 @@
|
|||
# The full repository name
|
||||
repo: gitea/gitea-vet
|
||||
|
||||
# Service type (gitea or github)
|
||||
service: gitea
|
||||
|
||||
# Base URL for Gitea instance if using gitea service type (optional)
|
||||
base-url: https://gitea.com
|
||||
|
||||
# Changelog groups and which labeled PRs to add to each group
|
||||
groups:
|
||||
-
|
||||
name: BREAKING
|
||||
labels:
|
||||
- breaking
|
||||
-
|
||||
name: FEATURES
|
||||
labels:
|
||||
- feature
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- bug
|
||||
-
|
||||
name: ENHANCEMENTS
|
||||
labels:
|
||||
- enhancement
|
||||
|
||||
# regex indicating which labels to skip for the changelog
|
||||
skip-labels: skip-changelog|backport\/.+
|
45
vendor/code.gitea.io/gitea-vet/.drone.yml
generated
vendored
45
vendor/code.gitea.io/gitea-vet/.drone.yml
generated
vendored
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
kind: pipeline
|
||||
name: compliance
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- pull_request
|
||||
|
||||
steps:
|
||||
- name: check
|
||||
pull: always
|
||||
image: golang:1.14
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
commands:
|
||||
- make build
|
||||
- make lint
|
||||
- make vet
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: build-master
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
pull: always
|
||||
image: techknowlogick/xgo:latest
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
commands:
|
||||
- make build
|
5
vendor/code.gitea.io/gitea-vet/.gitignore
generated
vendored
5
vendor/code.gitea.io/gitea-vet/.gitignore
generated
vendored
|
@ -1,5 +0,0 @@
|
|||
# GoLand
|
||||
.idea/
|
||||
|
||||
# Binaries
|
||||
/gitea-vet*
|
23
vendor/code.gitea.io/gitea-vet/.golangci.yml
generated
vendored
23
vendor/code.gitea.io/gitea-vet/.golangci.yml
generated
vendored
|
@ -1,23 +0,0 @@
|
|||
linters:
|
||||
enable:
|
||||
- deadcode
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- golint
|
||||
- gosimple
|
||||
- govet
|
||||
- maligned
|
||||
- misspell
|
||||
- prealloc
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
11
vendor/code.gitea.io/gitea-vet/CHANGELOG.md
generated
vendored
11
vendor/code.gitea.io/gitea-vet/CHANGELOG.md
generated
vendored
|
@ -1,11 +0,0 @@
|
|||
## [v0.2.1](https://gitea.com/gitea/gitea-vet/releases/tag/v0.2.1) - 2020-08-15
|
||||
|
||||
* BUGFIXES
|
||||
* Split migration check to Deps and Imports (#9)
|
||||
|
||||
## [0.2.0](https://gitea.com/gitea/gitea-vet/pulls?q=&type=all&state=closed&milestone=1272) - 2020-07-20
|
||||
|
||||
* FEATURES
|
||||
* Add migrations check (#5)
|
||||
* BUGFIXES
|
||||
* Correct Import Paths (#6)
|
19
vendor/code.gitea.io/gitea-vet/LICENSE
generated
vendored
19
vendor/code.gitea.io/gitea-vet/LICENSE
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2020 The Gitea Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
22
vendor/code.gitea.io/gitea-vet/Makefile
generated
vendored
22
vendor/code.gitea.io/gitea-vet/Makefile
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
GO ?= go
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
$(GO) build
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GO) fmt ./...
|
||||
|
||||
.PHONY: vet
|
||||
vet: build
|
||||
$(GO) vet ./...
|
||||
$(GO) vet -vettool=gitea-vet ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell $(GO) env GOPATH)/bin v1.24.0; \
|
||||
fi
|
||||
golangci-lint run --timeout 5m
|
11
vendor/code.gitea.io/gitea-vet/README.md
generated
vendored
11
vendor/code.gitea.io/gitea-vet/README.md
generated
vendored
|
@ -1,11 +0,0 @@
|
|||
# gitea-vet
|
||||
|
||||
[![Build Status](https://drone.gitea.com/api/badges/gitea/gitea-vet/status.svg)](https://drone.gitea.com/gitea/gitea-vet)
|
||||
|
||||
`go vet` tool for Gitea
|
||||
|
||||
| Analyzer | Description |
|
||||
|------------|-----------------------------------------------------------------------------|
|
||||
| Imports | Checks for import sorting. stdlib->code.gitea.io->other |
|
||||
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs` |
|
||||
| Migrations | Checks for black-listed packages in `code.gitea.io/gitea/models/migrations` |
|
46
vendor/code.gitea.io/gitea-vet/checks/imports.go
generated
vendored
46
vendor/code.gitea.io/gitea-vet/checks/imports.go
generated
vendored
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package checks
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var Imports = &analysis.Analyzer{
|
||||
Name: "imports",
|
||||
Doc: "check for import order",
|
||||
Run: runImports,
|
||||
}
|
||||
|
||||
func runImports(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, file := range pass.Files {
|
||||
level := 0
|
||||
for _, im := range file.Imports {
|
||||
var lvl int
|
||||
val := im.Path.Value
|
||||
switch {
|
||||
case importHasPrefix(val, "code.gitea.io"):
|
||||
lvl = 2
|
||||
case strings.Contains(val, "."):
|
||||
lvl = 3
|
||||
default:
|
||||
lvl = 1
|
||||
}
|
||||
|
||||
if lvl < level {
|
||||
pass.Reportf(file.Pos(), "Imports are sorted wrong")
|
||||
break
|
||||
}
|
||||
level = lvl
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func importHasPrefix(s, p string) bool {
|
||||
return strings.HasPrefix(s, "\""+p)
|
||||
}
|
73
vendor/code.gitea.io/gitea-vet/checks/license.go
generated
vendored
73
vendor/code.gitea.io/gitea-vet/checks/license.go
generated
vendored
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package checks
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var (
|
||||
header = regexp.MustCompile(`.*Copyright.*\d{4}.*(Gitea|Gogs)`)
|
||||
goGenerate = "//go:generate"
|
||||
buildTag = "// +build"
|
||||
)
|
||||
|
||||
var License = &analysis.Analyzer{
|
||||
Name: "license",
|
||||
Doc: "check for a copyright header",
|
||||
Run: runLicense,
|
||||
}
|
||||
|
||||
func runLicense(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, file := range pass.Files {
|
||||
if len(file.Comments) == 0 {
|
||||
pass.Reportf(file.Pos(), "Copyright not found")
|
||||
continue
|
||||
}
|
||||
|
||||
if len(file.Comments[0].List) == 0 {
|
||||
pass.Reportf(file.Pos(), "Copyright not found or wrong")
|
||||
continue
|
||||
}
|
||||
|
||||
commentGroup := 0
|
||||
if strings.HasPrefix(file.Comments[0].List[0].Text, goGenerate) {
|
||||
if len(file.Comments[0].List) > 1 {
|
||||
pass.Reportf(file.Pos(), "Must be an empty line between the go:generate and the Copyright")
|
||||
continue
|
||||
}
|
||||
commentGroup++
|
||||
}
|
||||
|
||||
if strings.HasPrefix(file.Comments[0].List[0].Text, buildTag) {
|
||||
commentGroup++
|
||||
}
|
||||
|
||||
if len(file.Comments) < commentGroup+1 {
|
||||
pass.Reportf(file.Pos(), "Copyright not found")
|
||||
continue
|
||||
}
|
||||
|
||||
if len(file.Comments[commentGroup].List) < 1 {
|
||||
pass.Reportf(file.Pos(), "Copyright not found or wrong")
|
||||
continue
|
||||
}
|
||||
|
||||
var check bool
|
||||
for _, comment := range file.Comments[commentGroup].List {
|
||||
if header.MatchString(comment.Text) {
|
||||
check = true
|
||||
}
|
||||
}
|
||||
|
||||
if !check {
|
||||
pass.Reportf(file.Pos(), "Copyright did not match check")
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
77
vendor/code.gitea.io/gitea-vet/checks/migrations.go
generated
vendored
77
vendor/code.gitea.io/gitea-vet/checks/migrations.go
generated
vendored
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var Migrations = &analysis.Analyzer{
|
||||
Name: "migrations",
|
||||
Doc: "check migrations for black-listed packages.",
|
||||
Run: checkMigrations,
|
||||
}
|
||||
|
||||
var (
|
||||
migrationDepBlockList = []string{
|
||||
"code.gitea.io/gitea/models",
|
||||
}
|
||||
migrationImpBlockList = []string{
|
||||
"code.gitea.io/gitea/modules/structs",
|
||||
}
|
||||
)
|
||||
|
||||
func checkMigrations(pass *analysis.Pass) (interface{}, error) {
|
||||
if !strings.EqualFold(pass.Pkg.Path(), "code.gitea.io/gitea/models/migrations") {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("go"); err != nil {
|
||||
return nil, errors.New("go was not found in the PATH")
|
||||
}
|
||||
|
||||
depsCmd := exec.Command("go", "list", "-f", `{{join .Deps "\n"}}`, "code.gitea.io/gitea/models/migrations")
|
||||
depsOut, err := depsCmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deps := strings.Split(string(depsOut), "\n")
|
||||
for _, dep := range deps {
|
||||
if stringInSlice(dep, migrationDepBlockList) {
|
||||
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot depend on the following packages: %s", migrationDepBlockList)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
impsCmd := exec.Command("go", "list", "-f", `{{join .Imports "\n"}}`, "code.gitea.io/gitea/models/migrations")
|
||||
impsOut, err := impsCmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imps := strings.Split(string(impsOut), "\n")
|
||||
for _, imp := range imps {
|
||||
if stringInSlice(imp, migrationImpBlockList) {
|
||||
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot import the following packages: %s", migrationImpBlockList)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func stringInSlice(needle string, haystack []string) bool {
|
||||
for _, h := range haystack {
|
||||
if strings.EqualFold(needle, h) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
5
vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
5
vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
|
@ -1,5 +0,0 @@
|
|||
module code.gitea.io/gitea-vet
|
||||
|
||||
go 1.14
|
||||
|
||||
require golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224
|
20
vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
20
vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
|
||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
19
vendor/code.gitea.io/gitea-vet/main.go
generated
vendored
19
vendor/code.gitea.io/gitea-vet/main.go
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea-vet/checks"
|
||||
|
||||
"golang.org/x/tools/go/analysis/unitchecker"
|
||||
)
|
||||
|
||||
func main() {
|
||||
unitchecker.Main(
|
||||
checks.Imports,
|
||||
checks.License,
|
||||
checks.Migrations,
|
||||
)
|
||||
}
|
20
vendor/code.gitea.io/sdk/gitea/LICENSE
generated
vendored
20
vendor/code.gitea.io/sdk/gitea/LICENSE
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2016 The Gitea Authors
|
||||
Copyright (c) 2014 The Gogs Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
47
vendor/code.gitea.io/sdk/gitea/admin_cron.go
generated
vendored
47
vendor/code.gitea.io/sdk/gitea/admin_cron.go
generated
vendored
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CronTask represents a Cron task
|
||||
type CronTask struct {
|
||||
Name string `json:"name"`
|
||||
Schedule string `json:"schedule"`
|
||||
Next time.Time `json:"next"`
|
||||
Prev time.Time `json:"prev"`
|
||||
ExecTimes int64 `json:"exec_times"`
|
||||
}
|
||||
|
||||
// ListCronTaskOptions list options for ListCronTasks
|
||||
type ListCronTaskOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListCronTasks list available cron tasks
|
||||
func (c *Client) ListCronTasks(opt ListCronTaskOptions) ([]*CronTask, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
ct := make([]*CronTask, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/cron?%s", opt.getURLQuery().Encode()), jsonHeader, nil, &ct)
|
||||
return ct, resp, err
|
||||
}
|
||||
|
||||
// RunCronTasks run a cron task
|
||||
func (c *Client) RunCronTasks(task string) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
39
vendor/code.gitea.io/sdk/gitea/admin_org.go
generated
vendored
39
vendor/code.gitea.io/sdk/gitea/admin_org.go
generated
vendored
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AdminListOrgsOptions options for listing admin's organizations
|
||||
type AdminListOrgsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// AdminListOrgs lists all orgs
|
||||
func (c *Client) AdminListOrgs(opt AdminListOrgsOptions) ([]*Organization, *Response, error) {
|
||||
opt.setDefaults()
|
||||
orgs := make([]*Organization, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||
return orgs, resp, err
|
||||
}
|
||||
|
||||
// AdminCreateOrg create an organization
|
||||
func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
org := new(Organization)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/orgs", user), jsonHeader, bytes.NewReader(body), org)
|
||||
return org, resp, err
|
||||
}
|
25
vendor/code.gitea.io/sdk/gitea/admin_repo.go
generated
vendored
25
vendor/code.gitea.io/sdk/gitea/admin_repo.go
generated
vendored
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AdminCreateRepo create a repo
|
||||
func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/repos", user), jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
130
vendor/code.gitea.io/sdk/gitea/admin_user.go
generated
vendored
130
vendor/code.gitea.io/sdk/gitea/admin_user.go
generated
vendored
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AdminListUsersOptions options for listing admin users
|
||||
type AdminListUsersOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// AdminListUsers lists all users
|
||||
func (c *Client) AdminListUsers(opt AdminListUsersOptions) ([]*User, *Response, error) {
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/admin/users?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// CreateUserOption create user options
|
||||
type CreateUserOption struct {
|
||||
SourceID int64 `json:"source_id"`
|
||||
LoginName string `json:"login_name"`
|
||||
Username string `json:"username"`
|
||||
FullName string `json:"full_name"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
MustChangePassword *bool `json:"must_change_password"`
|
||||
SendNotify bool `json:"send_notify"`
|
||||
Visibility *VisibleType `json:"visibility"`
|
||||
}
|
||||
|
||||
// Validate the CreateUserOption struct
|
||||
func (opt CreateUserOption) Validate() error {
|
||||
if len(opt.Email) == 0 {
|
||||
return fmt.Errorf("email is empty")
|
||||
}
|
||||
if len(opt.Username) == 0 {
|
||||
return fmt.Errorf("username is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminCreateUser create a user
|
||||
func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, *Response, error) {
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user := new(User)
|
||||
resp, err := c.getParsedResponse("POST", "/admin/users", jsonHeader, bytes.NewReader(body), user)
|
||||
return user, resp, err
|
||||
}
|
||||
|
||||
// EditUserOption edit user options
|
||||
type EditUserOption struct {
|
||||
SourceID int64 `json:"source_id"`
|
||||
LoginName string `json:"login_name"`
|
||||
Email *string `json:"email"`
|
||||
FullName *string `json:"full_name"`
|
||||
Password string `json:"password"`
|
||||
Description *string `json:"description"`
|
||||
MustChangePassword *bool `json:"must_change_password"`
|
||||
Website *string `json:"website"`
|
||||
Location *string `json:"location"`
|
||||
Active *bool `json:"active"`
|
||||
Admin *bool `json:"admin"`
|
||||
AllowGitHook *bool `json:"allow_git_hook"`
|
||||
AllowImportLocal *bool `json:"allow_import_local"`
|
||||
MaxRepoCreation *int `json:"max_repo_creation"`
|
||||
ProhibitLogin *bool `json:"prohibit_login"`
|
||||
AllowCreateOrganization *bool `json:"allow_create_organization"`
|
||||
Restricted *bool `json:"restricted"`
|
||||
Visibility *VisibleType `json:"visibility"`
|
||||
}
|
||||
|
||||
// AdminEditUser modify user informations
|
||||
func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/admin/users/%s", user), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// AdminDeleteUser delete one user according name
|
||||
func (c *Client) AdminDeleteUser(user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// AdminCreateUserPublicKey adds a public key for the user
|
||||
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key := new(PublicKey)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/keys", user), jsonHeader, bytes.NewReader(body), key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// AdminDeleteUserPublicKey deletes a user's public key
|
||||
func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil)
|
||||
return resp, err
|
||||
}
|
111
vendor/code.gitea.io/sdk/gitea/attachment.go
generated
vendored
111
vendor/code.gitea.io/sdk/gitea/attachment.go
generated
vendored
|
@ -1,111 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea // import "code.gitea.io/sdk/gitea"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Attachment a generic attachment
|
||||
type Attachment struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
DownloadCount int64 `json:"download_count"`
|
||||
Created time.Time `json:"created_at"`
|
||||
UUID string `json:"uuid"`
|
||||
DownloadURL string `json:"browser_download_url"`
|
||||
}
|
||||
|
||||
// ListReleaseAttachmentsOptions options for listing release's attachments
|
||||
type ListReleaseAttachmentsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListReleaseAttachments list release's attachments
|
||||
func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
attachments := make([]*Attachment, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d/assets?%s", user, repo, release, opt.getURLQuery().Encode()),
|
||||
nil, nil, &attachments)
|
||||
return attachments, resp, err
|
||||
}
|
||||
|
||||
// GetReleaseAttachment returns the requested attachment
|
||||
func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
a := new(Attachment)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id),
|
||||
nil, nil, &a)
|
||||
return a, resp, err
|
||||
}
|
||||
|
||||
// CreateReleaseAttachment creates an attachment for the given release
|
||||
func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Write file to body
|
||||
body := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("attachment", filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(part, file); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Send request
|
||||
attachment := new(Attachment)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release),
|
||||
http.Header{"Content-Type": {writer.FormDataContentType()}}, body, &attachment)
|
||||
return attachment, resp, err
|
||||
}
|
||||
|
||||
// EditAttachmentOptions options for editing attachments
|
||||
type EditAttachmentOptions struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// EditReleaseAttachment updates the given attachment with the given options
|
||||
func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&form)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
attach := new(Attachment)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, attachment), jsonHeader, bytes.NewReader(body), attach)
|
||||
return attach, resp, err
|
||||
}
|
||||
|
||||
// DeleteReleaseAttachment deletes the given attachment including the uploaded file
|
||||
func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
333
vendor/code.gitea.io/sdk/gitea/client.go
generated
vendored
333
vendor/code.gitea.io/sdk/gitea/client.go
generated
vendored
|
@ -1,333 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
var jsonHeader = http.Header{"content-type": []string{"application/json"}}
|
||||
|
||||
// Version return the library version
|
||||
func Version() string {
|
||||
return "0.14.0"
|
||||
}
|
||||
|
||||
// Client represents a thread-safe Gitea API client.
|
||||
type Client struct {
|
||||
url string
|
||||
accessToken string
|
||||
username string
|
||||
password string
|
||||
otp string
|
||||
sudo string
|
||||
debug bool
|
||||
client *http.Client
|
||||
ctx context.Context
|
||||
mutex sync.RWMutex
|
||||
serverVersion *version.Version
|
||||
getVersionOnce sync.Once
|
||||
}
|
||||
|
||||
// Response represents the gitea response
|
||||
type Response struct {
|
||||
*http.Response
|
||||
}
|
||||
|
||||
// NewClient initializes and returns a API client.
|
||||
// Usage of all gitea.Client methods is concurrency-safe.
|
||||
func NewClient(url string, options ...func(*Client)) (*Client, error) {
|
||||
client := &Client{
|
||||
url: strings.TrimSuffix(url, "/"),
|
||||
client: &http.Client{},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(client)
|
||||
}
|
||||
if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewClientWithHTTP creates an API client with a custom http client
|
||||
// Deprecated use SetHTTPClient option
|
||||
func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
|
||||
client, _ := NewClient(url, SetHTTPClient(httpClient))
|
||||
return client
|
||||
}
|
||||
|
||||
// SetHTTPClient is an option for NewClient to set custom http client
|
||||
func SetHTTPClient(httpClient *http.Client) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.SetHTTPClient(httpClient)
|
||||
}
|
||||
}
|
||||
|
||||
// SetHTTPClient replaces default http.Client with user given one.
|
||||
func (c *Client) SetHTTPClient(client *http.Client) {
|
||||
c.mutex.Lock()
|
||||
c.client = client
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetToken is an option for NewClient to set token
|
||||
func SetToken(token string) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.mutex.Lock()
|
||||
client.accessToken = token
|
||||
client.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// SetBasicAuth is an option for NewClient to set username and password
|
||||
func SetBasicAuth(username, password string) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.SetBasicAuth(username, password)
|
||||
}
|
||||
}
|
||||
|
||||
// SetBasicAuth sets username and password
|
||||
func (c *Client) SetBasicAuth(username, password string) {
|
||||
c.mutex.Lock()
|
||||
c.username, c.password = username, password
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetOTP is an option for NewClient to set OTP for 2FA
|
||||
func SetOTP(otp string) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.SetOTP(otp)
|
||||
}
|
||||
}
|
||||
|
||||
// SetOTP sets OTP for 2FA
|
||||
func (c *Client) SetOTP(otp string) {
|
||||
c.mutex.Lock()
|
||||
c.otp = otp
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetContext is an option for NewClient to set context
|
||||
func SetContext(ctx context.Context) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.SetContext(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// SetContext set context witch is used for http requests
|
||||
func (c *Client) SetContext(ctx context.Context) {
|
||||
c.mutex.Lock()
|
||||
c.ctx = ctx
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetSudo is an option for NewClient to set sudo header
|
||||
func SetSudo(sudo string) func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.SetSudo(sudo)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSudo sets username to impersonate.
|
||||
func (c *Client) SetSudo(sudo string) {
|
||||
c.mutex.Lock()
|
||||
c.sudo = sudo
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDebugMode is an option for NewClient to enable debug mode
|
||||
func SetDebugMode() func(client *Client) {
|
||||
return func(client *Client) {
|
||||
client.mutex.Lock()
|
||||
client.debug = true
|
||||
client.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) getWebResponse(method, path string, body io.Reader) ([]byte, *Response, error) {
|
||||
c.mutex.RLock()
|
||||
debug := c.debug
|
||||
if debug {
|
||||
fmt.Printf("%s: %s\nBody: %v\n", method, c.url+path, body)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(c.ctx, method, c.url+path, body)
|
||||
|
||||
client := c.client // client ref can change from this point on so safe it
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if debug {
|
||||
fmt.Printf("Response: %v\n\n", resp)
|
||||
}
|
||||
return data, &Response{resp}, nil
|
||||
}
|
||||
|
||||
func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) {
|
||||
c.mutex.RLock()
|
||||
debug := c.debug
|
||||
if debug {
|
||||
fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, body)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body)
|
||||
if err != nil {
|
||||
c.mutex.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
if len(c.accessToken) != 0 {
|
||||
req.Header.Set("Authorization", "token "+c.accessToken)
|
||||
}
|
||||
if len(c.otp) != 0 {
|
||||
req.Header.Set("X-GITEA-OTP", c.otp)
|
||||
}
|
||||
if len(c.username) != 0 {
|
||||
req.SetBasicAuth(c.username, c.password)
|
||||
}
|
||||
if len(c.sudo) != 0 {
|
||||
req.Header.Set("Sudo", c.sudo)
|
||||
}
|
||||
|
||||
client := c.client // client ref can change from this point on so safe it
|
||||
c.mutex.RUnlock()
|
||||
|
||||
for k, v := range header {
|
||||
req.Header[k] = v
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("Response: %v\n\n", resp)
|
||||
}
|
||||
return &Response{resp}, nil
|
||||
}
|
||||
|
||||
// Converts a response for a HTTP status code indicating an error condition
|
||||
// (non-2XX) to a well-known error value and response body. For non-problematic
|
||||
// (2XX) status codes nil will be returned. Note that on a non-2XX response, the
|
||||
// response body stream will have been read and, hence, is closed on return.
|
||||
func statusCodeToErr(resp *Response) (body []byte, err error) {
|
||||
// no error
|
||||
if resp.StatusCode/100 == 2 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//
|
||||
// error: body will be read for details
|
||||
//
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("body read on HTTP error %d: %v", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
switch resp.StatusCode {
|
||||
case 403:
|
||||
return data, errors.New("403 Forbidden")
|
||||
case 404:
|
||||
return data, errors.New("404 Not Found")
|
||||
case 409:
|
||||
return data, errors.New("409 Conflict")
|
||||
case 422:
|
||||
return data, fmt.Errorf("422 Unprocessable Entity: %s", string(data))
|
||||
}
|
||||
|
||||
path := resp.Request.URL.Path
|
||||
method := resp.Request.Method
|
||||
header := resp.Request.Header
|
||||
errMap := make(map[string]interface{})
|
||||
if err = json.Unmarshal(data, &errMap); err != nil {
|
||||
// when the JSON can't be parsed, data was probably empty or a
|
||||
// plain string, so we try to return a helpful error anyway
|
||||
return data, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
|
||||
}
|
||||
return data, errors.New(errMap["message"].(string))
|
||||
}
|
||||
|
||||
func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) {
|
||||
resp, err := c.doRequest(method, path, header, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// check for errors
|
||||
data, err := statusCodeToErr(resp)
|
||||
if err != nil {
|
||||
return data, resp, err
|
||||
}
|
||||
|
||||
// success (2XX), read body
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return data, resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) (*Response, error) {
|
||||
data, resp, err := c.getResponse(method, path, header, body)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, json.Unmarshal(data, obj)
|
||||
}
|
||||
|
||||
func (c *Client) getStatusCode(method, path string, header http.Header, body io.Reader) (int, *Response, error) {
|
||||
resp, err := c.doRequest(method, path, header, body)
|
||||
if err != nil {
|
||||
return -1, resp, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp.StatusCode, resp, nil
|
||||
}
|
||||
|
||||
// pathEscapeSegments escapes segments of a path while not escaping forward slash
|
||||
func pathEscapeSegments(path string) string {
|
||||
slice := strings.Split(path, "/")
|
||||
for index := range slice {
|
||||
slice[index] = url.PathEscape(slice[index])
|
||||
}
|
||||
escapedPath := strings.Join(slice, "/")
|
||||
return escapedPath
|
||||
}
|
||||
|
||||
// escapeValidatePathSegments is a help function to validate and encode url path segments
|
||||
func escapeValidatePathSegments(seg ...*string) error {
|
||||
for i := range seg {
|
||||
if seg[i] == nil || len(*seg[i]) == 0 {
|
||||
return fmt.Errorf("path segment [%d] is empty", i)
|
||||
}
|
||||
*seg[i] = url.PathEscape(*seg[i])
|
||||
}
|
||||
return nil
|
||||
}
|
5
vendor/code.gitea.io/sdk/gitea/doc.go
generated
vendored
5
vendor/code.gitea.io/sdk/gitea/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
|||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea // import "code.gitea.io/sdk/gitea"
|
49
vendor/code.gitea.io/sdk/gitea/fork.go
generated
vendored
49
vendor/code.gitea.io/sdk/gitea/fork.go
generated
vendored
|
@ -1,49 +0,0 @@
|
|||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ListForksOptions options for listing repository's forks
|
||||
type ListForksOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListForks list a repository's forks
|
||||
func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
forks := make([]*Repository, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/forks?%s", user, repo, opt.getURLQuery().Encode()),
|
||||
nil, nil, &forks)
|
||||
return forks, resp, err
|
||||
}
|
||||
|
||||
// CreateForkOption options for creating a fork
|
||||
type CreateForkOption struct {
|
||||
// organization name, if forking into an organization
|
||||
Organization *string `json:"organization"`
|
||||
}
|
||||
|
||||
// CreateFork create a fork of a repository
|
||||
func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(form)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fork := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/forks", user, repo), jsonHeader, bytes.NewReader(body), &fork)
|
||||
return fork, resp, err
|
||||
}
|
28
vendor/code.gitea.io/sdk/gitea/git_blob.go
generated
vendored
28
vendor/code.gitea.io/sdk/gitea/git_blob.go
generated
vendored
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GitBlobResponse represents a git blob
|
||||
type GitBlobResponse struct {
|
||||
Content string `json:"content"`
|
||||
Encoding string `json:"encoding"`
|
||||
URL string `json:"url"`
|
||||
SHA string `json:"sha"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// GetBlob get the blob of a repository file
|
||||
func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
blob := new(GitBlobResponse)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob)
|
||||
return blob, resp, err
|
||||
}
|
71
vendor/code.gitea.io/sdk/gitea/git_hook.go
generated
vendored
71
vendor/code.gitea.io/sdk/gitea/git_hook.go
generated
vendored
|
@ -1,71 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GitHook represents a Git repository hook
|
||||
type GitHook struct {
|
||||
Name string `json:"name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Content string `json:"content,omitempty"`
|
||||
}
|
||||
|
||||
// ListRepoGitHooksOptions options for listing repository's githooks
|
||||
type ListRepoGitHooksOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoGitHooks list all the Git hooks of one repository
|
||||
func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
hooks := make([]*GitHook, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||
return hooks, resp, err
|
||||
}
|
||||
|
||||
// GetRepoGitHook get a Git hook of a repository
|
||||
func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := new(GitHook)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h)
|
||||
return h, resp, err
|
||||
}
|
||||
|
||||
// EditGitHookOption options when modifying one Git hook
|
||||
type EditGitHookOption struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// EditRepoGitHook modify one Git hook of a repository
|
||||
func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteRepoGitHook delete one Git hook from a repository
|
||||
func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
9
vendor/code.gitea.io/sdk/gitea/go.mod
generated
vendored
9
vendor/code.gitea.io/sdk/gitea/go.mod
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
module code.gitea.io/sdk/gitea
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
code.gitea.io/gitea-vet v0.2.1 // indirect
|
||||
github.com/hashicorp/go-version v1.2.1
|
||||
github.com/stretchr/testify v1.4.0
|
||||
)
|
33
vendor/code.gitea.io/sdk/gitea/go.sum
generated
vendored
33
vendor/code.gitea.io/sdk/gitea/go.sum
generated
vendored
|
@ -1,33 +0,0 @@
|
|||
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
|
||||
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
|
||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
20
vendor/code.gitea.io/sdk/gitea/helper.go
generated
vendored
20
vendor/code.gitea.io/sdk/gitea/helper.go
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
// OptionalBool convert a bool to a bool reference
|
||||
func OptionalBool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// OptionalString convert a string to a string reference
|
||||
func OptionalString(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// OptionalInt64 convert a int64 to a int64 reference
|
||||
func OptionalInt64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
194
vendor/code.gitea.io/sdk/gitea/hook.go
generated
vendored
194
vendor/code.gitea.io/sdk/gitea/hook.go
generated
vendored
|
@ -1,194 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Hook a hook is a web hook when one repository changed
|
||||
type Hook struct {
|
||||
ID int64 `json:"id"`
|
||||
Type string `json:"type"`
|
||||
URL string `json:"-"`
|
||||
Config map[string]string `json:"config"`
|
||||
Events []string `json:"events"`
|
||||
Active bool `json:"active"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
Created time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// HookType represent all webhook types gitea currently offer
|
||||
type HookType string
|
||||
|
||||
const (
|
||||
// HookTypeDingtalk webhook that dingtalk understand
|
||||
HookTypeDingtalk HookType = "dingtalk"
|
||||
// HookTypeDiscord webhook that discord understand
|
||||
HookTypeDiscord HookType = "discord"
|
||||
// HookTypeGitea webhook that gitea understand
|
||||
HookTypeGitea HookType = "gitea"
|
||||
// HookTypeGogs webhook that gogs understand
|
||||
HookTypeGogs HookType = "gogs"
|
||||
// HookTypeMsteams webhook that msteams understand
|
||||
HookTypeMsteams HookType = "msteams"
|
||||
// HookTypeSlack webhook that slack understand
|
||||
HookTypeSlack HookType = "slack"
|
||||
// HookTypeTelegram webhook that telegram understand
|
||||
HookTypeTelegram HookType = "telegram"
|
||||
// HookTypeFeishu webhook that feishu understand
|
||||
HookTypeFeishu HookType = "feishu"
|
||||
)
|
||||
|
||||
// ListHooksOptions options for listing hooks
|
||||
type ListHooksOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListOrgHooks list all the hooks of one organization
|
||||
func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
hooks := make([]*Hook, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||
return hooks, resp, err
|
||||
}
|
||||
|
||||
// ListRepoHooks list all the hooks of one repository
|
||||
func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
hooks := make([]*Hook, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
|
||||
return hooks, resp, err
|
||||
}
|
||||
|
||||
// GetOrgHook get a hook of an organization
|
||||
func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := new(Hook)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h)
|
||||
return h, resp, err
|
||||
}
|
||||
|
||||
// GetRepoHook get a hook of a repository
|
||||
func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := new(Hook)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h)
|
||||
return h, resp, err
|
||||
}
|
||||
|
||||
// CreateHookOption options when create a hook
|
||||
type CreateHookOption struct {
|
||||
Type HookType `json:"type"`
|
||||
Config map[string]string `json:"config"`
|
||||
Events []string `json:"events"`
|
||||
BranchFilter string `json:"branch_filter"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// Validate the CreateHookOption struct
|
||||
func (opt CreateHookOption) Validate() error {
|
||||
if len(opt.Type) == 0 {
|
||||
return fmt.Errorf("hook type needed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrgHook create one hook for an organization, with options
|
||||
func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := new(Hook)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/orgs/%s/hooks", org), jsonHeader, bytes.NewReader(body), h)
|
||||
return h, resp, err
|
||||
}
|
||||
|
||||
// CreateRepoHook create one hook for a repository, with options
|
||||
func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := new(Hook)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/hooks", user, repo), jsonHeader, bytes.NewReader(body), h)
|
||||
return h, resp, err
|
||||
}
|
||||
|
||||
// EditHookOption options when modify one hook
|
||||
type EditHookOption struct {
|
||||
Config map[string]string `json:"config"`
|
||||
Events []string `json:"events"`
|
||||
BranchFilter string `json:"branch_filter"`
|
||||
Active *bool `json:"active"`
|
||||
}
|
||||
|
||||
// EditOrgHook modify one hook of an organization, with hook id and options
|
||||
func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// EditRepoHook modify one hook of a repository, with hook id and options
|
||||
func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteOrgHook delete one hook from an organization, with hook id
|
||||
func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteRepoHook delete one hook from a repository, with hook id
|
||||
func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
288
vendor/code.gitea.io/sdk/gitea/issue.go
generated
vendored
288
vendor/code.gitea.io/sdk/gitea/issue.go
generated
vendored
|
@ -1,288 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PullRequestMeta PR info if an issue is a PR
|
||||
type PullRequestMeta struct {
|
||||
HasMerged bool `json:"merged"`
|
||||
Merged *time.Time `json:"merged_at"`
|
||||
}
|
||||
|
||||
// RepositoryMeta basic repository information
|
||||
type RepositoryMeta struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
FullName string `json:"full_name"`
|
||||
}
|
||||
|
||||
// Issue represents an issue in a repository
|
||||
type Issue struct {
|
||||
ID int64 `json:"id"`
|
||||
URL string `json:"url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Index int64 `json:"number"`
|
||||
Poster *User `json:"user"`
|
||||
OriginalAuthor string `json:"original_author"`
|
||||
OriginalAuthorID int64 `json:"original_author_id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Ref string `json:"ref"`
|
||||
Labels []*Label `json:"labels"`
|
||||
Milestone *Milestone `json:"milestone"`
|
||||
Assignees []*User `json:"assignees"`
|
||||
// Whether the issue is open or closed
|
||||
State StateType `json:"state"`
|
||||
IsLocked bool `json:"is_locked"`
|
||||
Comments int `json:"comments"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
Closed *time.Time `json:"closed_at"`
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
PullRequest *PullRequestMeta `json:"pull_request"`
|
||||
Repository *RepositoryMeta `json:"repository"`
|
||||
}
|
||||
|
||||
// ListIssueOption list issue options
|
||||
type ListIssueOption struct {
|
||||
ListOptions
|
||||
State StateType
|
||||
Type IssueType
|
||||
Labels []string
|
||||
Milestones []string
|
||||
KeyWord string
|
||||
Since time.Time
|
||||
Before time.Time
|
||||
// filter by created by username
|
||||
CreatedBy string
|
||||
// filter by assigned to username
|
||||
AssignedBy string
|
||||
// filter by username mentioned
|
||||
MentionedBy string
|
||||
}
|
||||
|
||||
// StateType issue state type
|
||||
type StateType string
|
||||
|
||||
const (
|
||||
// StateOpen pr/issue is opend
|
||||
StateOpen StateType = "open"
|
||||
// StateClosed pr/issue is closed
|
||||
StateClosed StateType = "closed"
|
||||
// StateAll is all
|
||||
StateAll StateType = "all"
|
||||
)
|
||||
|
||||
// IssueType is issue a pull or only an issue
|
||||
type IssueType string
|
||||
|
||||
const (
|
||||
// IssueTypeAll pr and issue
|
||||
IssueTypeAll IssueType = ""
|
||||
// IssueTypeIssue only issues
|
||||
IssueTypeIssue IssueType = "issues"
|
||||
// IssueTypePull only pulls
|
||||
IssueTypePull IssueType = "pulls"
|
||||
)
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListIssueOption) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
|
||||
if len(opt.State) > 0 {
|
||||
query.Add("state", string(opt.State))
|
||||
}
|
||||
|
||||
if len(opt.Labels) > 0 {
|
||||
query.Add("labels", strings.Join(opt.Labels, ","))
|
||||
}
|
||||
|
||||
if len(opt.KeyWord) > 0 {
|
||||
query.Add("q", opt.KeyWord)
|
||||
}
|
||||
|
||||
query.Add("type", string(opt.Type))
|
||||
|
||||
if len(opt.Milestones) > 0 {
|
||||
query.Add("milestones", strings.Join(opt.Milestones, ","))
|
||||
}
|
||||
|
||||
if !opt.Since.IsZero() {
|
||||
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||
}
|
||||
if !opt.Before.IsZero() {
|
||||
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if len(opt.CreatedBy) > 0 {
|
||||
query.Add("created_by", opt.CreatedBy)
|
||||
}
|
||||
if len(opt.AssignedBy) > 0 {
|
||||
query.Add("assigned_by", opt.AssignedBy)
|
||||
}
|
||||
if len(opt.MentionedBy) > 0 {
|
||||
query.Add("mentioned_by", opt.MentionedBy)
|
||||
}
|
||||
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListIssues returns all issues assigned the authenticated user
|
||||
func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, *Response, error) {
|
||||
opt.setDefaults()
|
||||
issues := make([]*Issue, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse("/repos/issues/search")
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
|
||||
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil {
|
||||
for i := 0; i < len(issues); i++ {
|
||||
if issues[i].Repository != nil {
|
||||
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range issues {
|
||||
c.issueBackwardsCompatibility(issues[i])
|
||||
}
|
||||
return issues, resp, err
|
||||
}
|
||||
|
||||
// ListRepoIssues returns all issues for a given repository
|
||||
func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
issues := make([]*Issue, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
|
||||
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil {
|
||||
for i := 0; i < len(issues); i++ {
|
||||
if issues[i].Repository != nil {
|
||||
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range issues {
|
||||
c.issueBackwardsCompatibility(issues[i])
|
||||
}
|
||||
return issues, resp, err
|
||||
}
|
||||
|
||||
// GetIssue returns a single issue for a given repository
|
||||
func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
issue := new(Issue)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue)
|
||||
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil {
|
||||
issue.Repository.Owner = strings.Split(issue.Repository.FullName, "/")[0]
|
||||
}
|
||||
c.issueBackwardsCompatibility(issue)
|
||||
return issue, resp, err
|
||||
}
|
||||
|
||||
// CreateIssueOption options to create one issue
|
||||
type CreateIssueOption struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Ref string `json:"ref"`
|
||||
Assignees []string `json:"assignees"`
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
// milestone id
|
||||
Milestone int64 `json:"milestone"`
|
||||
// list of label ids
|
||||
Labels []int64 `json:"labels"`
|
||||
Closed bool `json:"closed"`
|
||||
}
|
||||
|
||||
// Validate the CreateIssueOption struct
|
||||
func (opt CreateIssueOption) Validate() error {
|
||||
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateIssue create a new issue for a given repository
|
||||
func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
issue := new(Issue)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues", owner, repo),
|
||||
jsonHeader, bytes.NewReader(body), issue)
|
||||
c.issueBackwardsCompatibility(issue)
|
||||
return issue, resp, err
|
||||
}
|
||||
|
||||
// EditIssueOption options for editing an issue
|
||||
type EditIssueOption struct {
|
||||
Title string `json:"title"`
|
||||
Body *string `json:"body"`
|
||||
Ref *string `json:"ref"`
|
||||
Assignees []string `json:"assignees"`
|
||||
Milestone *int64 `json:"milestone"`
|
||||
State *StateType `json:"state"`
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
RemoveDeadline *bool `json:"unset_due_date"`
|
||||
}
|
||||
|
||||
// Validate the EditIssueOption struct
|
||||
func (opt EditIssueOption) Validate() error {
|
||||
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditIssue modify an existing issue for a given repository
|
||||
func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
issue := new(Issue)
|
||||
resp, err := c.getParsedResponse("PATCH",
|
||||
fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body), issue)
|
||||
c.issueBackwardsCompatibility(issue)
|
||||
return issue, resp, err
|
||||
}
|
||||
|
||||
func (c *Client) issueBackwardsCompatibility(issue *Issue) {
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_12_0) != nil {
|
||||
c.mutex.RLock()
|
||||
issue.HTMLURL = fmt.Sprintf("%s/%s/issues/%d", c.url, issue.Repository.FullName, issue.Index)
|
||||
c.mutex.RUnlock()
|
||||
}
|
||||
}
|
154
vendor/code.gitea.io/sdk/gitea/issue_comment.go
generated
vendored
154
vendor/code.gitea.io/sdk/gitea/issue_comment.go
generated
vendored
|
@ -1,154 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Comment represents a comment on a commit or issue
|
||||
type Comment struct {
|
||||
ID int64 `json:"id"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
PRURL string `json:"pull_request_url"`
|
||||
IssueURL string `json:"issue_url"`
|
||||
Poster *User `json:"user"`
|
||||
OriginalAuthor string `json:"original_author"`
|
||||
OriginalAuthorID int64 `json:"original_author_id"`
|
||||
Body string `json:"body"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ListIssueCommentOptions list comment options
|
||||
type ListIssueCommentOptions struct {
|
||||
ListOptions
|
||||
Since time.Time
|
||||
Before time.Time
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListIssueCommentOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if !opt.Since.IsZero() {
|
||||
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||
}
|
||||
if !opt.Before.IsZero() {
|
||||
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListIssueComments list comments on an issue.
|
||||
func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
comments := make([]*Comment, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &comments)
|
||||
return comments, resp, err
|
||||
}
|
||||
|
||||
// ListRepoIssueComments list comments for a given repo.
|
||||
func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
comments := make([]*Comment, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &comments)
|
||||
return comments, resp, err
|
||||
}
|
||||
|
||||
// GetIssueComment get a comment for a given repo by id.
|
||||
func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
comment := new(Comment)
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return comment, nil, err
|
||||
}
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, id), nil, nil, &comment)
|
||||
return comment, resp, err
|
||||
}
|
||||
|
||||
// CreateIssueCommentOption options for creating a comment on an issue
|
||||
type CreateIssueCommentOption struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// Validate the CreateIssueCommentOption struct
|
||||
func (opt CreateIssueCommentOption) Validate() error {
|
||||
if len(opt.Body) == 0 {
|
||||
return fmt.Errorf("body is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateIssueComment create comment on an issue.
|
||||
func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
comment := new(Comment)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index), jsonHeader, bytes.NewReader(body), comment)
|
||||
return comment, resp, err
|
||||
}
|
||||
|
||||
// EditIssueCommentOption options for editing a comment
|
||||
type EditIssueCommentOption struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// Validate the EditIssueCommentOption struct
|
||||
func (opt EditIssueCommentOption) Validate() error {
|
||||
if len(opt.Body) == 0 {
|
||||
return fmt.Errorf("body is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditIssueComment edits an issue comment.
|
||||
func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
comment := new(Comment)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), jsonHeader, bytes.NewReader(body), comment)
|
||||
return comment, resp, err
|
||||
}
|
||||
|
||||
// DeleteIssueComment deletes an issue comment.
|
||||
func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil)
|
||||
return resp, err
|
||||
}
|
211
vendor/code.gitea.io/sdk/gitea/issue_label.go
generated
vendored
211
vendor/code.gitea.io/sdk/gitea/issue_label.go
generated
vendored
|
@ -1,211 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Label a label to an issue or a pr
|
||||
type Label struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
// example: 00aabb
|
||||
Color string `json:"color"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// ListLabelsOptions options for listing repository's labels
|
||||
type ListLabelsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoLabels list labels of one repository
|
||||
func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
labels := make([]*Label, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels)
|
||||
return labels, resp, err
|
||||
}
|
||||
|
||||
// GetRepoLabel get one label of repository by repo it
|
||||
func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
label := new(Label)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label)
|
||||
return label, resp, err
|
||||
}
|
||||
|
||||
// CreateLabelOption options for creating a label
|
||||
type CreateLabelOption struct {
|
||||
Name string `json:"name"`
|
||||
// example: #00aabb
|
||||
Color string `json:"color"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// Validate the CreateLabelOption struct
|
||||
func (opt CreateLabelOption) Validate() error {
|
||||
aw, err := regexp.MatchString("^#?[0-9,a-f,A-F]{6}$", opt.Color)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !aw {
|
||||
return fmt.Errorf("invalid color format")
|
||||
}
|
||||
if len(strings.TrimSpace(opt.Name)) == 0 {
|
||||
return fmt.Errorf("empty name not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateLabel create one label of repository
|
||||
func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(opt.Color) == 6 {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
opt.Color = "#" + opt.Color
|
||||
}
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
label := new(Label)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/labels", owner, repo),
|
||||
jsonHeader, bytes.NewReader(body), label)
|
||||
return label, resp, err
|
||||
}
|
||||
|
||||
// EditLabelOption options for editing a label
|
||||
type EditLabelOption struct {
|
||||
Name *string `json:"name"`
|
||||
Color *string `json:"color"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
// Validate the EditLabelOption struct
|
||||
func (opt EditLabelOption) Validate() error {
|
||||
if opt.Color != nil {
|
||||
aw, err := regexp.MatchString("^#?[0-9,a-f,A-F]{6}$", *opt.Color)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !aw {
|
||||
return fmt.Errorf("invalid color format")
|
||||
}
|
||||
}
|
||||
if opt.Name != nil {
|
||||
if len(strings.TrimSpace(*opt.Name)) == 0 {
|
||||
return fmt.Errorf("empty name not allowed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditLabel modify one label with options
|
||||
func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
label := new(Label)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), label)
|
||||
return label, resp, err
|
||||
}
|
||||
|
||||
// DeleteLabel delete one label of repository by id
|
||||
func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetIssueLabels get labels of one issue via issue id
|
||||
func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
labels := make([]*Label, 0, 5)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels)
|
||||
return labels, resp, err
|
||||
}
|
||||
|
||||
// IssueLabelsOption a collection of labels
|
||||
type IssueLabelsOption struct {
|
||||
// list of label IDs
|
||||
Labels []int64 `json:"labels"`
|
||||
}
|
||||
|
||||
// AddIssueLabels add one or more labels to one issue
|
||||
func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var labels []*Label
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels)
|
||||
return labels, resp, err
|
||||
}
|
||||
|
||||
// ReplaceIssueLabels replace old labels of issue with new labels
|
||||
func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var labels []*Label
|
||||
resp, err := c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels)
|
||||
return labels, resp, err
|
||||
}
|
||||
|
||||
// DeleteIssueLabel delete one label of one issue by issue id and label id
|
||||
// TODO: maybe we need delete by label name and issue id
|
||||
func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ClearIssueLabels delete all the labels of one issue.
|
||||
func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil)
|
||||
return resp, err
|
||||
}
|
237
vendor/code.gitea.io/sdk/gitea/issue_milestone.go
generated
vendored
237
vendor/code.gitea.io/sdk/gitea/issue_milestone.go
generated
vendored
|
@ -1,237 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Milestone milestone is a collection of issues on one repository
|
||||
type Milestone struct {
|
||||
ID int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
State StateType `json:"state"`
|
||||
OpenIssues int `json:"open_issues"`
|
||||
ClosedIssues int `json:"closed_issues"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated *time.Time `json:"updated_at"`
|
||||
Closed *time.Time `json:"closed_at"`
|
||||
Deadline *time.Time `json:"due_on"`
|
||||
}
|
||||
|
||||
// ListMilestoneOption list milestone options
|
||||
type ListMilestoneOption struct {
|
||||
ListOptions
|
||||
// open, closed, all
|
||||
State StateType
|
||||
Name string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListMilestoneOption) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if opt.State != "" {
|
||||
query.Add("state", string(opt.State))
|
||||
}
|
||||
if len(opt.Name) != 0 {
|
||||
query.Add("name", opt.Name)
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListRepoMilestones list all the milestones of one repository
|
||||
func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
milestones := make([]*Milestone, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/milestones", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &milestones)
|
||||
return milestones, resp, err
|
||||
}
|
||||
|
||||
// GetMilestone get one milestone by repo name and milestone id
|
||||
func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
milestone := new(Milestone)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone)
|
||||
return milestone, resp, err
|
||||
}
|
||||
|
||||
// GetMilestoneByName get one milestone by repo and milestone name
|
||||
func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone, *Response, error) {
|
||||
if c.CheckServerVersionConstraint(">=1.13") != nil {
|
||||
// backwards compatibility mode
|
||||
m, resp, err := c.resolveMilestoneByName(owner, repo, name)
|
||||
return m, resp, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
milestone := new(Milestone)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone)
|
||||
return milestone, resp, err
|
||||
}
|
||||
|
||||
// CreateMilestoneOption options for creating a milestone
|
||||
type CreateMilestoneOption struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
State StateType `json:"state"`
|
||||
Deadline *time.Time `json:"due_on"`
|
||||
}
|
||||
|
||||
// Validate the CreateMilestoneOption struct
|
||||
func (opt CreateMilestoneOption) Validate() error {
|
||||
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateMilestone create one milestone with options
|
||||
func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
milestone := new(Milestone)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), jsonHeader, bytes.NewReader(body), milestone)
|
||||
|
||||
// make creating closed milestones need gitea >= v1.13.0
|
||||
// this make it backwards compatible
|
||||
if err == nil && opt.State == StateClosed && milestone.State != StateClosed {
|
||||
closed := StateClosed
|
||||
return c.EditMilestone(owner, repo, milestone.ID, EditMilestoneOption{
|
||||
State: &closed,
|
||||
})
|
||||
}
|
||||
|
||||
return milestone, resp, err
|
||||
}
|
||||
|
||||
// EditMilestoneOption options for editing a milestone
|
||||
type EditMilestoneOption struct {
|
||||
Title string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
State *StateType `json:"state"`
|
||||
Deadline *time.Time `json:"due_on"`
|
||||
}
|
||||
|
||||
// Validate the EditMilestoneOption struct
|
||||
func (opt EditMilestoneOption) Validate() error {
|
||||
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditMilestone modify milestone with options
|
||||
func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
milestone := new(Milestone)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), milestone)
|
||||
return milestone, resp, err
|
||||
}
|
||||
|
||||
// EditMilestoneByName modify milestone with options
|
||||
func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMilestoneOption) (*Milestone, *Response, error) {
|
||||
if c.CheckServerVersionConstraint(">=1.13") != nil {
|
||||
// backwards compatibility mode
|
||||
m, _, err := c.resolveMilestoneByName(owner, repo, name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return c.EditMilestone(owner, repo, m.ID, opt)
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
milestone := new(Milestone)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), jsonHeader, bytes.NewReader(body), milestone)
|
||||
return milestone, resp, err
|
||||
}
|
||||
|
||||
// DeleteMilestone delete one milestone by id
|
||||
func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteMilestoneByName delete one milestone by name
|
||||
func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Response, error) {
|
||||
if c.CheckServerVersionConstraint(">=1.13") != nil {
|
||||
// backwards compatibility mode
|
||||
m, _, err := c.resolveMilestoneByName(owner, repo, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.DeleteMilestone(owner, repo, m.ID)
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// resolveMilestoneByName is a fallback method to find milestone id by name
|
||||
func (c *Client) resolveMilestoneByName(owner, repo, name string) (*Milestone, *Response, error) {
|
||||
for i := 1; ; i++ {
|
||||
miles, resp, err := c.ListRepoMilestones(owner, repo, ListMilestoneOption{
|
||||
ListOptions: ListOptions{
|
||||
Page: i,
|
||||
},
|
||||
State: "all",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(miles) == 0 {
|
||||
return nil, nil, fmt.Errorf("milestone '%s' do not exist", name)
|
||||
}
|
||||
for _, m := range miles {
|
||||
if strings.ToLower(strings.TrimSpace(m.Title)) == strings.ToLower(strings.TrimSpace(name)) {
|
||||
return m, resp, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
vendor/code.gitea.io/sdk/gitea/issue_reaction.go
generated
vendored
104
vendor/code.gitea.io/sdk/gitea/issue_reaction.go
generated
vendored
|
@ -1,104 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Reaction contain one reaction
|
||||
type Reaction struct {
|
||||
User *User `json:"user"`
|
||||
Reaction string `json:"content"`
|
||||
Created time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// GetIssueReactions get a list reactions of an issue
|
||||
func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reactions := make([]*Reaction, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), nil, nil, &reactions)
|
||||
return reactions, resp, err
|
||||
}
|
||||
|
||||
// GetIssueCommentReactions get a list of reactions from a comment of an issue
|
||||
func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reactions := make([]*Reaction, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID), nil, nil, &reactions)
|
||||
return reactions, resp, err
|
||||
}
|
||||
|
||||
// editReactionOption contain the reaction type
|
||||
type editReactionOption struct {
|
||||
Reaction string `json:"content"`
|
||||
}
|
||||
|
||||
// PostIssueReaction add a reaction to an issue
|
||||
func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reactionResponse := new(Reaction)
|
||||
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body), reactionResponse)
|
||||
return reactionResponse, resp, err
|
||||
}
|
||||
|
||||
// DeleteIssueReaction remove a reaction from an issue
|
||||
func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// PostIssueCommentReaction add a reaction to a comment of an issue
|
||||
func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reactionResponse := new(Reaction)
|
||||
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
|
||||
jsonHeader, bytes.NewReader(body), reactionResponse)
|
||||
return reactionResponse, resp, err
|
||||
}
|
||||
|
||||
// DeleteIssueCommentReaction remove a reaction from a comment of an issue
|
||||
func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
|
||||
jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
57
vendor/code.gitea.io/sdk/gitea/issue_stopwatch.go
generated
vendored
57
vendor/code.gitea.io/sdk/gitea/issue_stopwatch.go
generated
vendored
|
@ -1,57 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StopWatch represents a running stopwatch of an issue / pr
|
||||
type StopWatch struct {
|
||||
Created time.Time `json:"created"`
|
||||
Seconds int64 `json:"seconds"`
|
||||
Duration string `json:"duration"`
|
||||
IssueIndex int64 `json:"issue_index"`
|
||||
IssueTitle string `json:"issue_title"`
|
||||
RepoOwnerName string `json:"repo_owner_name"`
|
||||
RepoName string `json:"repo_name"`
|
||||
}
|
||||
|
||||
// GetMyStopwatches list all stopwatches
|
||||
func (c *Client) GetMyStopwatches() ([]*StopWatch, *Response, error) {
|
||||
stopwatches := make([]*StopWatch, 0, 1)
|
||||
resp, err := c.getParsedResponse("GET", "/user/stopwatches", nil, nil, &stopwatches)
|
||||
return stopwatches, resp, err
|
||||
}
|
||||
|
||||
// DeleteIssueStopwatch delete / cancel a specific stopwatch
|
||||
func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// StartIssueStopWatch starts a stopwatch for an existing issue for a given
|
||||
// repository
|
||||
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// StopIssueStopWatch stops an existing stopwatch for an issue in a given
|
||||
// repository
|
||||
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil)
|
||||
return resp, err
|
||||
}
|
87
vendor/code.gitea.io/sdk/gitea/issue_subscription.go
generated
vendored
87
vendor/code.gitea.io/sdk/gitea/issue_subscription.go
generated
vendored
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetIssueSubscribers get list of users who subscribed on an issue
|
||||
func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
subscribers := make([]*User, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions", owner, repo, index), nil, nil, &subscribers)
|
||||
return subscribers, resp, err
|
||||
}
|
||||
|
||||
// AddIssueSubscription Subscribe user to issue
|
||||
func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if status == http.StatusCreated {
|
||||
return resp, nil
|
||||
}
|
||||
if status == http.StatusOK {
|
||||
return resp, fmt.Errorf("already subscribed")
|
||||
}
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
|
||||
// DeleteIssueSubscription unsubscribe user from issue
|
||||
func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if status == http.StatusCreated {
|
||||
return resp, nil
|
||||
}
|
||||
if status == http.StatusOK {
|
||||
return resp, fmt.Errorf("already unsubscribed")
|
||||
}
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
|
||||
// CheckIssueSubscription check if current user is subscribed to an issue
|
||||
func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
wi := new(WatchInfo)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/check", owner, repo, index), nil, nil, wi)
|
||||
return wi, resp, err
|
||||
}
|
||||
|
||||
// IssueSubscribe subscribe current user to an issue
|
||||
func (c *Client) IssueSubscribe(owner, repo string, index int64) (*Response, error) {
|
||||
u, _, err := c.GetMyUserInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.AddIssueSubscription(owner, repo, index, u.UserName)
|
||||
}
|
||||
|
||||
// IssueUnSubscribe unsubscribe current user from an issue
|
||||
func (c *Client) IssueUnSubscribe(owner, repo string, index int64) (*Response, error) {
|
||||
u, _, err := c.GetMyUserInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.DeleteIssueSubscription(owner, repo, index, u.UserName)
|
||||
}
|
142
vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
generated
vendored
142
vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
generated
vendored
|
@ -1,142 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TrackedTime worked time for an issue / pr
|
||||
type TrackedTime struct {
|
||||
ID int64 `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
// Time in seconds
|
||||
Time int64 `json:"time"`
|
||||
// deprecated (only for backwards compatibility)
|
||||
UserID int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
// deprecated (only for backwards compatibility)
|
||||
IssueID int64 `json:"issue_id"`
|
||||
Issue *Issue `json:"issue"`
|
||||
}
|
||||
|
||||
// ListTrackedTimesOptions options for listing repository's tracked times
|
||||
type ListTrackedTimesOptions struct {
|
||||
ListOptions
|
||||
Since time.Time
|
||||
Before time.Time
|
||||
// User filter is only used by ListRepoTrackedTimes !!!
|
||||
User string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListTrackedTimesOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
|
||||
if !opt.Since.IsZero() {
|
||||
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||
}
|
||||
if !opt.Before.IsZero() {
|
||||
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if len(opt.User) != 0 {
|
||||
query.Add("user", opt.User)
|
||||
}
|
||||
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListRepoTrackedTimes list tracked times of a repository
|
||||
func (c *Client) ListRepoTrackedTimes(owner, repo string, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/times", owner, repo))
|
||||
opt.setDefaults()
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
times := make([]*TrackedTime, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, ×)
|
||||
return times, resp, err
|
||||
}
|
||||
|
||||
// GetMyTrackedTimes list tracked times of the current user
|
||||
func (c *Client) GetMyTrackedTimes() ([]*TrackedTime, *Response, error) {
|
||||
times := make([]*TrackedTime, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", "/user/times", jsonHeader, nil, ×)
|
||||
return times, resp, err
|
||||
}
|
||||
|
||||
// AddTimeOption options for adding time to an issue
|
||||
type AddTimeOption struct {
|
||||
// time in seconds
|
||||
Time int64 `json:"time"`
|
||||
// optional
|
||||
Created time.Time `json:"created"`
|
||||
// optional
|
||||
User string `json:"user_name"`
|
||||
}
|
||||
|
||||
// Validate the AddTimeOption struct
|
||||
func (opt AddTimeOption) Validate() error {
|
||||
if opt.Time == 0 {
|
||||
return fmt.Errorf("no time to add")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTime adds time to issue with the given index
|
||||
func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(TrackedTime)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body), t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// ListIssueTrackedTimes list tracked times of a single issue for a given repository
|
||||
func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index))
|
||||
opt.setDefaults()
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
times := make([]*TrackedTime, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, ×)
|
||||
return times, resp, err
|
||||
}
|
||||
|
||||
// ResetIssueTime reset tracked time of a single issue for a given repository
|
||||
func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteTime delete a specific tracked time by id of a single issue for a given repository
|
||||
func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
42
vendor/code.gitea.io/sdk/gitea/list_options.go
generated
vendored
42
vendor/code.gitea.io/sdk/gitea/list_options.go
generated
vendored
|
@ -1,42 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const defaultPageSize = 10
|
||||
const maxPageSize = 50
|
||||
|
||||
// ListOptions options for using Gitea's API pagination
|
||||
type ListOptions struct {
|
||||
Page int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
func (o ListOptions) getURLQuery() url.Values {
|
||||
query := make(url.Values)
|
||||
query.Add("page", fmt.Sprintf("%d", o.Page))
|
||||
query.Add("limit", fmt.Sprintf("%d", o.PageSize))
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
// setDefaults set default pagination options if none or wrong are set
|
||||
// if you set -1 as page it will set all to 0
|
||||
func (o *ListOptions) setDefaults() {
|
||||
if o.Page < 0 {
|
||||
o.Page, o.PageSize = 0, 0
|
||||
return
|
||||
} else if o.Page == 0 {
|
||||
o.Page = 1
|
||||
}
|
||||
|
||||
if o.PageSize < 0 || o.PageSize > maxPageSize {
|
||||
o.PageSize = defaultPageSize
|
||||
}
|
||||
}
|
241
vendor/code.gitea.io/sdk/gitea/notifications.go
generated
vendored
241
vendor/code.gitea.io/sdk/gitea/notifications.go
generated
vendored
|
@ -1,241 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
var (
|
||||
version1_12_3, _ = version.NewVersion("1.12.3")
|
||||
)
|
||||
|
||||
// NotificationThread expose Notification on API
|
||||
type NotificationThread struct {
|
||||
ID int64 `json:"id"`
|
||||
Repository *Repository `json:"repository"`
|
||||
Subject *NotificationSubject `json:"subject"`
|
||||
Unread bool `json:"unread"`
|
||||
Pinned bool `json:"pinned"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// NotificationSubject contains the notification subject (Issue/Pull/Commit)
|
||||
type NotificationSubject struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
LatestCommentURL string `json:"latest_comment_url"`
|
||||
Type NotifySubjectType `json:"type"`
|
||||
State NotifySubjectState `json:"state"`
|
||||
}
|
||||
|
||||
// NotifyStatus notification status type
|
||||
type NotifyStatus string
|
||||
|
||||
const (
|
||||
// NotifyStatusUnread was not read
|
||||
NotifyStatusUnread NotifyStatus = "unread"
|
||||
// NotifyStatusRead was already read by user
|
||||
NotifyStatusRead NotifyStatus = "read"
|
||||
// NotifyStatusPinned notification is pinned by user
|
||||
NotifyStatusPinned NotifyStatus = "pinned"
|
||||
)
|
||||
|
||||
// NotifySubjectType represent type of notification subject
|
||||
type NotifySubjectType string
|
||||
|
||||
const (
|
||||
// NotifySubjectIssue an issue is subject of an notification
|
||||
NotifySubjectIssue NotifySubjectType = "Issue"
|
||||
// NotifySubjectPull an pull is subject of an notification
|
||||
NotifySubjectPull NotifySubjectType = "Pull"
|
||||
// NotifySubjectCommit an commit is subject of an notification
|
||||
NotifySubjectCommit NotifySubjectType = "Commit"
|
||||
// NotifySubjectRepository an repository is subject of an notification
|
||||
NotifySubjectRepository NotifySubjectType = "Repository"
|
||||
)
|
||||
|
||||
// NotifySubjectState reflect state of notification subject
|
||||
type NotifySubjectState string
|
||||
|
||||
const (
|
||||
// NotifySubjectOpen if subject is a pull/issue and is open at the moment
|
||||
NotifySubjectOpen NotifySubjectState = "open"
|
||||
// NotifySubjectClosed if subject is a pull/issue and is closed at the moment
|
||||
NotifySubjectClosed NotifySubjectState = "closed"
|
||||
// NotifySubjectMerged if subject is a pull and got merged
|
||||
NotifySubjectMerged NotifySubjectState = "merged"
|
||||
)
|
||||
|
||||
// ListNotificationOptions represents the filter options
|
||||
type ListNotificationOptions struct {
|
||||
ListOptions
|
||||
Since time.Time
|
||||
Before time.Time
|
||||
Status []NotifyStatus
|
||||
SubjectTypes []NotifySubjectType
|
||||
}
|
||||
|
||||
// MarkNotificationOptions represents the filter & modify options
|
||||
type MarkNotificationOptions struct {
|
||||
LastReadAt time.Time
|
||||
Status []NotifyStatus
|
||||
ToStatus NotifyStatus
|
||||
}
|
||||
|
||||
// QueryEncode encode options to url query
|
||||
func (opt *ListNotificationOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if !opt.Since.IsZero() {
|
||||
query.Add("since", opt.Since.Format(time.RFC3339))
|
||||
}
|
||||
if !opt.Before.IsZero() {
|
||||
query.Add("before", opt.Before.Format(time.RFC3339))
|
||||
}
|
||||
for _, s := range opt.Status {
|
||||
query.Add("status-types", string(s))
|
||||
}
|
||||
for _, s := range opt.SubjectTypes {
|
||||
query.Add("subject-type", string(s))
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// Validate the CreateUserOption struct
|
||||
func (opt ListNotificationOptions) Validate(c *Client) error {
|
||||
if len(opt.Status) != 0 {
|
||||
return c.checkServerVersionGreaterThanOrEqual(version1_12_3)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryEncode encode options to url query
|
||||
func (opt *MarkNotificationOptions) QueryEncode() string {
|
||||
query := make(url.Values)
|
||||
if !opt.LastReadAt.IsZero() {
|
||||
query.Add("last_read_at", opt.LastReadAt.Format(time.RFC3339))
|
||||
}
|
||||
for _, s := range opt.Status {
|
||||
query.Add("status-types", string(s))
|
||||
}
|
||||
if len(opt.ToStatus) != 0 {
|
||||
query.Add("to-status", string(opt.ToStatus))
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// Validate the CreateUserOption struct
|
||||
func (opt MarkNotificationOptions) Validate(c *Client) error {
|
||||
if len(opt.Status) != 0 || len(opt.ToStatus) != 0 {
|
||||
return c.checkServerVersionGreaterThanOrEqual(version1_12_3)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckNotifications list users's notification threads
|
||||
func (c *Client) CheckNotifications() (int64, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
new := struct {
|
||||
New int64 `json:"new"`
|
||||
}{}
|
||||
|
||||
resp, err := c.getParsedResponse("GET", "/notifications/new", jsonHeader, nil, &new)
|
||||
return new.New, resp, err
|
||||
}
|
||||
|
||||
// GetNotification get notification thread by ID
|
||||
func (c *Client) GetNotification(id int64) (*NotificationThread, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
thread := new(NotificationThread)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/notifications/threads/%d", id), nil, nil, thread)
|
||||
return thread, resp, err
|
||||
}
|
||||
|
||||
// ReadNotification mark notification thread as read by ID
|
||||
// It optionally takes a second argument if status has to be set other than 'read'
|
||||
func (c *Client) ReadNotification(id int64, status ...NotifyStatus) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link := fmt.Sprintf("/notifications/threads/%d", id)
|
||||
if len(status) != 0 {
|
||||
link += fmt.Sprintf("?to-status=%s", status[0])
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", link, nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListNotifications list users's notification threads
|
||||
func (c *Client) ListNotifications(opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse("/notifications")
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
threads := make([]*NotificationThread, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
|
||||
return threads, resp, err
|
||||
}
|
||||
|
||||
// ReadNotifications mark notification threads as read
|
||||
func (c *Client) ReadNotifications(opt MarkNotificationOptions) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link, _ := url.Parse("/notifications")
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
_, resp, err := c.getResponse("PUT", link.String(), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListRepoNotifications list users's notification threads on a specific repo
|
||||
func (c *Client) ListRepoNotifications(owner, repo string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
threads := make([]*NotificationThread, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
|
||||
return threads, resp, err
|
||||
}
|
||||
|
||||
// ReadRepoNotifications mark notification threads as read on a specific repo
|
||||
func (c *Client) ReadRepoNotifications(owner, repo string, opt MarkNotificationOptions) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
_, resp, err := c.getResponse("PUT", link.String(), nil, nil)
|
||||
return resp, err
|
||||
}
|
91
vendor/code.gitea.io/sdk/gitea/oauth2.go
generated
vendored
91
vendor/code.gitea.io/sdk/gitea/oauth2.go
generated
vendored
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Oauth2 represents an Oauth2 Application
|
||||
type Oauth2 struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// ListOauth2Option for listing Oauth2 Applications
|
||||
type ListOauth2Option struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// CreateOauth2Option required options for creating an Application
|
||||
type CreateOauth2Option struct {
|
||||
Name string `json:"name"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
}
|
||||
|
||||
// CreateOauth2 create an Oauth2 Application and returns a completed Oauth2 object.
|
||||
func (c *Client) CreateOauth2(opt CreateOauth2Option) (*Oauth2, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
oauth := new(Oauth2)
|
||||
resp, err := c.getParsedResponse("POST", "/user/applications/oauth2", jsonHeader, bytes.NewReader(body), oauth)
|
||||
return oauth, resp, err
|
||||
}
|
||||
|
||||
// UpdateOauth2 a specific Oauth2 Application by ID and return a completed Oauth2 object.
|
||||
func (c *Client) UpdateOauth2(oauth2id int64, opt CreateOauth2Option) (*Oauth2, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
oauth := new(Oauth2)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), jsonHeader, bytes.NewReader(body), oauth)
|
||||
return oauth, resp, err
|
||||
}
|
||||
|
||||
// GetOauth2 a specific Oauth2 Application by ID.
|
||||
func (c *Client) GetOauth2(oauth2id int64) (*Oauth2, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
oauth2s := &Oauth2{}
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil, &oauth2s)
|
||||
return oauth2s, resp, err
|
||||
}
|
||||
|
||||
// ListOauth2 all of your Oauth2 Applications.
|
||||
func (c *Client) ListOauth2(opt ListOauth2Option) ([]*Oauth2, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
oauth2s := make([]*Oauth2, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2?%s", opt.getURLQuery().Encode()), nil, nil, &oauth2s)
|
||||
return oauth2s, resp, err
|
||||
}
|
||||
|
||||
// DeleteOauth2 delete an Oauth2 application by ID
|
||||
func (c *Client) DeleteOauth2(oauth2id int64) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil)
|
||||
return resp, err
|
||||
}
|
155
vendor/code.gitea.io/sdk/gitea/org.go
generated
vendored
155
vendor/code.gitea.io/sdk/gitea/org.go
generated
vendored
|
@ -1,155 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Organization represents an organization
|
||||
type Organization struct {
|
||||
ID int64 `json:"id"`
|
||||
UserName string `json:"username"`
|
||||
FullName string `json:"full_name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Visibility string `json:"visibility"`
|
||||
}
|
||||
|
||||
// VisibleType defines the visibility
|
||||
type VisibleType string
|
||||
|
||||
const (
|
||||
// VisibleTypePublic Visible for everyone
|
||||
VisibleTypePublic VisibleType = "public"
|
||||
|
||||
// VisibleTypeLimited Visible for every connected user
|
||||
VisibleTypeLimited VisibleType = "limited"
|
||||
|
||||
// VisibleTypePrivate Visible only for organization's members
|
||||
VisibleTypePrivate VisibleType = "private"
|
||||
)
|
||||
|
||||
// ListOrgsOptions options for listing organizations
|
||||
type ListOrgsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListMyOrgs list all of current user's organizations
|
||||
func (c *Client) ListMyOrgs(opt ListOrgsOptions) ([]*Organization, *Response, error) {
|
||||
opt.setDefaults()
|
||||
orgs := make([]*Organization, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||
return orgs, resp, err
|
||||
}
|
||||
|
||||
// ListUserOrgs list all of some user's organizations
|
||||
func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
orgs := make([]*Organization, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs)
|
||||
return orgs, resp, err
|
||||
}
|
||||
|
||||
// GetOrg get one organization by name
|
||||
func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
org := new(Organization)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org)
|
||||
return org, resp, err
|
||||
}
|
||||
|
||||
// CreateOrgOption options for creating an organization
|
||||
type CreateOrgOption struct {
|
||||
Name string `json:"username"`
|
||||
FullName string `json:"full_name"`
|
||||
Description string `json:"description"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Visibility VisibleType `json:"visibility"`
|
||||
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||
}
|
||||
|
||||
// checkVisibilityOpt check if mode exist
|
||||
func checkVisibilityOpt(v VisibleType) bool {
|
||||
return v == VisibleTypePublic || v == VisibleTypeLimited || v == VisibleTypePrivate
|
||||
}
|
||||
|
||||
// Validate the CreateOrgOption struct
|
||||
func (opt CreateOrgOption) Validate() error {
|
||||
if len(opt.Name) == 0 {
|
||||
return fmt.Errorf("empty org name")
|
||||
}
|
||||
if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) {
|
||||
return fmt.Errorf("infalid bisibility option")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrg creates an organization
|
||||
func (c *Client) CreateOrg(opt CreateOrgOption) (*Organization, *Response, error) {
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
org := new(Organization)
|
||||
resp, err := c.getParsedResponse("POST", "/orgs", jsonHeader, bytes.NewReader(body), org)
|
||||
return org, resp, err
|
||||
}
|
||||
|
||||
// EditOrgOption options for editing an organization
|
||||
type EditOrgOption struct {
|
||||
FullName string `json:"full_name"`
|
||||
Description string `json:"description"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Visibility VisibleType `json:"visibility"`
|
||||
}
|
||||
|
||||
// Validate the EditOrgOption struct
|
||||
func (opt EditOrgOption) Validate() error {
|
||||
if len(opt.Visibility) != 0 && !checkVisibilityOpt(opt.Visibility) {
|
||||
return fmt.Errorf("infalid bisibility option")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditOrg modify one organization via options
|
||||
func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteOrg deletes an organization
|
||||
func (c *Client) DeleteOrg(orgname string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&orgname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
119
vendor/code.gitea.io/sdk/gitea/org_member.go
generated
vendored
119
vendor/code.gitea.io/sdk/gitea/org_member.go
generated
vendored
|
@ -1,119 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// DeleteOrgMembership remove a member from an organization
|
||||
func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListOrgMembershipOption list OrgMembership options
|
||||
type ListOrgMembershipOption struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListOrgMembership list an organization's members
|
||||
func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", org))
|
||||
link.RawQuery = opt.getURLQuery().Encode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// ListPublicOrgMembership list an organization's members
|
||||
func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", org))
|
||||
link.RawQuery = opt.getURLQuery().Encode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// CheckOrgMembership Check if a user is a member of an organization
|
||||
func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
switch status {
|
||||
case http.StatusNoContent:
|
||||
return true, resp, nil
|
||||
case http.StatusNotFound:
|
||||
return false, resp, nil
|
||||
default:
|
||||
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPublicOrgMembership Check if a user is a member of an organization
|
||||
func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
switch status {
|
||||
case http.StatusNoContent:
|
||||
return true, resp, nil
|
||||
case http.StatusNotFound:
|
||||
return false, resp, nil
|
||||
default:
|
||||
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
}
|
||||
|
||||
// SetPublicOrgMembership publicize/conceal a user's membership
|
||||
func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
status int
|
||||
err error
|
||||
resp *Response
|
||||
)
|
||||
if visible {
|
||||
status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||
} else {
|
||||
status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
switch status {
|
||||
case http.StatusNoContent:
|
||||
return resp, nil
|
||||
case http.StatusNotFound:
|
||||
return resp, fmt.Errorf("forbidden")
|
||||
default:
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
}
|
242
vendor/code.gitea.io/sdk/gitea/org_team.go
generated
vendored
242
vendor/code.gitea.io/sdk/gitea/org_team.go
generated
vendored
|
@ -1,242 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Team represents a team in an organization
|
||||
type Team struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Organization *Organization `json:"organization"`
|
||||
Permission AccessMode `json:"permission"`
|
||||
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||
Units []RepoUnitType `json:"units"`
|
||||
}
|
||||
|
||||
// RepoUnitType represent all unit types of a repo gitea currently offer
|
||||
type RepoUnitType string
|
||||
|
||||
const (
|
||||
// RepoUnitCode represent file view of a repository
|
||||
RepoUnitCode RepoUnitType = "repo.code"
|
||||
// RepoUnitIssues represent issues of a repository
|
||||
RepoUnitIssues RepoUnitType = "repo.issues"
|
||||
// RepoUnitPulls represent pulls of a repository
|
||||
RepoUnitPulls RepoUnitType = "repo.pulls"
|
||||
// RepoUnitExtIssues represent external issues of a repository
|
||||
RepoUnitExtIssues RepoUnitType = "repo.ext_issues"
|
||||
// RepoUnitWiki represent wiki of a repository
|
||||
RepoUnitWiki RepoUnitType = "repo.wiki"
|
||||
// RepoUnitExtWiki represent external wiki of a repository
|
||||
RepoUnitExtWiki RepoUnitType = "repo.ext_wiki"
|
||||
// RepoUnitReleases represent releases of a repository
|
||||
RepoUnitReleases RepoUnitType = "repo.releases"
|
||||
// RepoUnitProjects represent projects of a repository
|
||||
RepoUnitProjects RepoUnitType = "repo.projects"
|
||||
)
|
||||
|
||||
// ListTeamsOptions options for listing teams
|
||||
type ListTeamsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListOrgTeams lists all teams of an organization
|
||||
func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
teams := make([]*Team, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams)
|
||||
return teams, resp, err
|
||||
}
|
||||
|
||||
// ListMyTeams lists all the teams of the current user
|
||||
func (c *Client) ListMyTeams(opt *ListTeamsOptions) ([]*Team, *Response, error) {
|
||||
opt.setDefaults()
|
||||
teams := make([]*Team, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/teams?%s", opt.getURLQuery().Encode()), nil, nil, &teams)
|
||||
return teams, resp, err
|
||||
}
|
||||
|
||||
// GetTeam gets a team by ID
|
||||
func (c *Client) GetTeam(id int64) (*Team, *Response, error) {
|
||||
t := new(Team)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d", id), nil, nil, t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// CreateTeamOption options for creating a team
|
||||
type CreateTeamOption struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Permission AccessMode `json:"permission"`
|
||||
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||
Units []RepoUnitType `json:"units"`
|
||||
}
|
||||
|
||||
// Validate the CreateTeamOption struct
|
||||
func (opt CreateTeamOption) Validate() error {
|
||||
if opt.Permission == AccessModeOwner {
|
||||
opt.Permission = AccessModeAdmin
|
||||
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
|
||||
return fmt.Errorf("permission mode invalid")
|
||||
}
|
||||
if len(opt.Name) == 0 {
|
||||
return fmt.Errorf("name required")
|
||||
}
|
||||
if len(opt.Name) > 30 {
|
||||
return fmt.Errorf("name to long")
|
||||
}
|
||||
if len(opt.Description) > 255 {
|
||||
return fmt.Errorf("description to long")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeam creates a team for an organization
|
||||
func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(Team)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/orgs/%s/teams", org), jsonHeader, bytes.NewReader(body), t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// EditTeamOption options for editing a team
|
||||
type EditTeamOption struct {
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
Permission AccessMode `json:"permission"`
|
||||
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
|
||||
IncludesAllRepositories *bool `json:"includes_all_repositories"`
|
||||
Units []RepoUnitType `json:"units"`
|
||||
}
|
||||
|
||||
// Validate the EditTeamOption struct
|
||||
func (opt EditTeamOption) Validate() error {
|
||||
if opt.Permission == AccessModeOwner {
|
||||
opt.Permission = AccessModeAdmin
|
||||
} else if opt.Permission != AccessModeRead && opt.Permission != AccessModeWrite && opt.Permission != AccessModeAdmin {
|
||||
return fmt.Errorf("permission mode invalid")
|
||||
}
|
||||
if len(opt.Name) == 0 {
|
||||
return fmt.Errorf("name required")
|
||||
}
|
||||
if len(opt.Name) > 30 {
|
||||
return fmt.Errorf("name to long")
|
||||
}
|
||||
if opt.Description != nil && len(*opt.Description) > 255 {
|
||||
return fmt.Errorf("description to long")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditTeam edits a team of an organization
|
||||
func (c *Client) EditTeam(id int64, opt EditTeamOption) (*Response, error) {
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PATCH", fmt.Sprintf("/teams/%d", id), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteTeam deletes a team of an organization
|
||||
func (c *Client) DeleteTeam(id int64) (*Response, error) {
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d", id), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListTeamMembersOptions options for listing team's members
|
||||
type ListTeamMembersOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListTeamMembers lists all members of a team
|
||||
func (c *Client) ListTeamMembers(id int64, opt ListTeamMembersOptions) ([]*User, *Response, error) {
|
||||
opt.setDefaults()
|
||||
members := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members?%s", id, opt.getURLQuery().Encode()), nil, nil, &members)
|
||||
return members, resp, err
|
||||
}
|
||||
|
||||
// GetTeamMember gets a member of a team
|
||||
func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
m := new(User)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m)
|
||||
return m, resp, err
|
||||
}
|
||||
|
||||
// AddTeamMember adds a member to a team
|
||||
func (c *Client) AddTeamMember(id int64, user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// RemoveTeamMember removes a member from a team
|
||||
func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ListTeamRepositoriesOptions options for listing team's repositories
|
||||
type ListTeamRepositoriesOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListTeamRepositories lists all repositories of a team
|
||||
func (c *Client) ListTeamRepositories(id int64, opt ListTeamRepositoriesOptions) ([]*Repository, *Response, error) {
|
||||
opt.setDefaults()
|
||||
repos := make([]*Repository, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/repos?%s", id, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// AddTeamRepository adds a repository to a team
|
||||
func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// RemoveTeamRepository removes a repository from a team
|
||||
func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&org, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
|
||||
return resp, err
|
||||
}
|
324
vendor/code.gitea.io/sdk/gitea/pull.go
generated
vendored
324
vendor/code.gitea.io/sdk/gitea/pull.go
generated
vendored
|
@ -1,324 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PRBranchInfo information about a branch
|
||||
type PRBranchInfo struct {
|
||||
Name string `json:"label"`
|
||||
Ref string `json:"ref"`
|
||||
Sha string `json:"sha"`
|
||||
RepoID int64 `json:"repo_id"`
|
||||
Repository *Repository `json:"repo"`
|
||||
}
|
||||
|
||||
// PullRequest represents a pull request
|
||||
type PullRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
URL string `json:"url"`
|
||||
Index int64 `json:"number"`
|
||||
Poster *User `json:"user"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Labels []*Label `json:"labels"`
|
||||
Milestone *Milestone `json:"milestone"`
|
||||
Assignee *User `json:"assignee"`
|
||||
Assignees []*User `json:"assignees"`
|
||||
State StateType `json:"state"`
|
||||
IsLocked bool `json:"is_locked"`
|
||||
Comments int `json:"comments"`
|
||||
|
||||
HTMLURL string `json:"html_url"`
|
||||
DiffURL string `json:"diff_url"`
|
||||
PatchURL string `json:"patch_url"`
|
||||
|
||||
Mergeable bool `json:"mergeable"`
|
||||
HasMerged bool `json:"merged"`
|
||||
Merged *time.Time `json:"merged_at"`
|
||||
MergedCommitID *string `json:"merge_commit_sha"`
|
||||
MergedBy *User `json:"merged_by"`
|
||||
|
||||
Base *PRBranchInfo `json:"base"`
|
||||
Head *PRBranchInfo `json:"head"`
|
||||
MergeBase string `json:"merge_base"`
|
||||
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
Created *time.Time `json:"created_at"`
|
||||
Updated *time.Time `json:"updated_at"`
|
||||
Closed *time.Time `json:"closed_at"`
|
||||
}
|
||||
|
||||
// ListPullRequestsOptions options for listing pull requests
|
||||
type ListPullRequestsOptions struct {
|
||||
ListOptions
|
||||
State StateType `json:"state"`
|
||||
// oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority
|
||||
Sort string
|
||||
Milestone int64
|
||||
}
|
||||
|
||||
// MergeStyle is used specify how a pull is merged
|
||||
type MergeStyle string
|
||||
|
||||
const (
|
||||
// MergeStyleMerge merge pull as usual
|
||||
MergeStyleMerge MergeStyle = "merge"
|
||||
// MergeStyleRebase rebase pull
|
||||
MergeStyleRebase MergeStyle = "rebase"
|
||||
// MergeStyleRebaseMerge rebase and merge pull
|
||||
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
||||
// MergeStyleSquash squash and merge pull
|
||||
MergeStyleSquash MergeStyle = "squash"
|
||||
)
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListPullRequestsOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if len(opt.State) > 0 {
|
||||
query.Add("state", string(opt.State))
|
||||
}
|
||||
if len(opt.Sort) > 0 {
|
||||
query.Add("sort", opt.Sort)
|
||||
}
|
||||
if opt.Milestone > 0 {
|
||||
query.Add("milestone", fmt.Sprintf("%d", opt.Milestone))
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListRepoPullRequests list PRs of one repository
|
||||
func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
prs := make([]*PullRequest, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs)
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||
for i := range prs {
|
||||
if err := fixPullHeadSha(c, prs[i]); err != nil {
|
||||
return prs, resp, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return prs, resp, err
|
||||
}
|
||||
|
||||
// GetPullRequest get information of one PR
|
||||
func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pr := new(PullRequest)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr)
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||
if err := fixPullHeadSha(c, pr); err != nil {
|
||||
return pr, resp, err
|
||||
}
|
||||
}
|
||||
return pr, resp, err
|
||||
}
|
||||
|
||||
// CreatePullRequestOption options when creating a pull request
|
||||
type CreatePullRequestOption struct {
|
||||
Head string `json:"head"`
|
||||
Base string `json:"base"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Assignee string `json:"assignee"`
|
||||
Assignees []string `json:"assignees"`
|
||||
Milestone int64 `json:"milestone"`
|
||||
Labels []int64 `json:"labels"`
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
}
|
||||
|
||||
// CreatePullRequest create pull request with options
|
||||
func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pr := new(PullRequest)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls", owner, repo),
|
||||
jsonHeader, bytes.NewReader(body), pr)
|
||||
return pr, resp, err
|
||||
}
|
||||
|
||||
// EditPullRequestOption options when modify pull request
|
||||
type EditPullRequestOption struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Base string `json:"base"`
|
||||
Assignee string `json:"assignee"`
|
||||
Assignees []string `json:"assignees"`
|
||||
Milestone int64 `json:"milestone"`
|
||||
Labels []int64 `json:"labels"`
|
||||
State *StateType `json:"state"`
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
}
|
||||
|
||||
// Validate the EditPullRequestOption struct
|
||||
func (opt EditPullRequestOption) Validate(c *Client) error {
|
||||
if len(opt.Title) != 0 && len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
if len(opt.Base) != 0 {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return fmt.Errorf("can not change base gitea to old")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditPullRequest modify pull request with PR id and options
|
||||
func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pr := new(PullRequest)
|
||||
resp, err := c.getParsedResponse("PATCH",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body), pr)
|
||||
return pr, resp, err
|
||||
}
|
||||
|
||||
// MergePullRequestOption options when merging a pull request
|
||||
type MergePullRequestOption struct {
|
||||
Style MergeStyle `json:"Do"`
|
||||
Title string `json:"MergeTitleField"`
|
||||
Message string `json:"MergeMessageField"`
|
||||
}
|
||||
|
||||
// Validate the MergePullRequestOption struct
|
||||
func (opt MergePullRequestOption) Validate(c *Client) error {
|
||||
if opt.Style == MergeStyleSquash {
|
||||
if err := c.CheckServerVersionConstraint(">=1.11.5"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergePullRequest merge a PR to repository by PR id
|
||||
func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), jsonHeader, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
return status == 200, resp, nil
|
||||
}
|
||||
|
||||
// IsPullRequestMerged test if one PR is merged to one repository
|
||||
func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
|
||||
return status == 204, resp, nil
|
||||
}
|
||||
|
||||
// getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR
|
||||
func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &kind); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
r, _, err2 := c.GetRepo(owner, repo)
|
||||
if err2 != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if r.Private {
|
||||
return nil, nil, err
|
||||
}
|
||||
return c.getWebResponse("GET", fmt.Sprintf("/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil)
|
||||
}
|
||||
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d.%s", owner, repo, index, kind), nil, nil)
|
||||
}
|
||||
|
||||
// GetPullRequestPatch gets the .patch file as bytes for a PR
|
||||
func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *Response, error) {
|
||||
return c.getPullRequestDiffOrPatch(owner, repo, "patch", index)
|
||||
}
|
||||
|
||||
// GetPullRequestDiff gets the .diff file as bytes for a PR
|
||||
func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) {
|
||||
return c.getPullRequestDiffOrPatch(owner, repo, "diff", index)
|
||||
}
|
||||
|
||||
// ListPullRequestCommitsOptions options for listing pull requests
|
||||
type ListPullRequestCommitsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListPullRequestCommits list commits for a pull request
|
||||
func (c *Client) ListPullRequestCommits(owner, repo string, index int64, opt ListPullRequestCommitsOptions) ([]*Commit, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/commits", owner, repo, index))
|
||||
opt.setDefaults()
|
||||
commits := make([]*Commit, 0, opt.PageSize)
|
||||
link.RawQuery = opt.getURLQuery().Encode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &commits)
|
||||
return commits, resp, err
|
||||
}
|
||||
|
||||
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
|
||||
// When no head sha is available, this is because the branch got deleted in the base repo.
|
||||
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
|
||||
// which stays available to resolve the commit sha. This is fixed for gitea >= 1.14.0
|
||||
func fixPullHeadSha(client *Client, pr *PullRequest) error {
|
||||
if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil &&
|
||||
pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
|
||||
owner := pr.Base.Repository.Owner.UserName
|
||||
repo := pr.Base.Repository.Name
|
||||
refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(refs) == 0 {
|
||||
return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
|
||||
}
|
||||
pr.Head.Sha = refs[0].Object.SHA
|
||||
}
|
||||
return nil
|
||||
}
|
325
vendor/code.gitea.io/sdk/gitea/pull_review.go
generated
vendored
325
vendor/code.gitea.io/sdk/gitea/pull_review.go
generated
vendored
|
@ -1,325 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReviewStateType review state type
|
||||
type ReviewStateType string
|
||||
|
||||
const (
|
||||
// ReviewStateApproved pr is approved
|
||||
ReviewStateApproved ReviewStateType = "APPROVED"
|
||||
// ReviewStatePending pr state is pending
|
||||
ReviewStatePending ReviewStateType = "PENDING"
|
||||
// ReviewStateComment is a comment review
|
||||
ReviewStateComment ReviewStateType = "COMMENT"
|
||||
// ReviewStateRequestChanges changes for pr are requested
|
||||
ReviewStateRequestChanges ReviewStateType = "REQUEST_CHANGES"
|
||||
// ReviewStateRequestReview review is requested from user
|
||||
ReviewStateRequestReview ReviewStateType = "REQUEST_REVIEW"
|
||||
// ReviewStateUnknown state of pr is unknown
|
||||
ReviewStateUnknown ReviewStateType = ""
|
||||
)
|
||||
|
||||
// PullReview represents a pull request review
|
||||
type PullReview struct {
|
||||
ID int64 `json:"id"`
|
||||
Reviewer *User `json:"user"`
|
||||
ReviewerTeam *Team `json:"team"`
|
||||
State ReviewStateType `json:"state"`
|
||||
Body string `json:"body"`
|
||||
CommitID string `json:"commit_id"`
|
||||
// Stale indicates if the pull has changed since the review
|
||||
Stale bool `json:"stale"`
|
||||
// Official indicates if the review counts towards the required approval limit, if PR base is a protected branch
|
||||
Official bool `json:"official"`
|
||||
Dismissed bool `json:"dismissed"`
|
||||
CodeCommentsCount int `json:"comments_count"`
|
||||
Submitted time.Time `json:"submitted_at"`
|
||||
|
||||
HTMLURL string `json:"html_url"`
|
||||
HTMLPullURL string `json:"pull_request_url"`
|
||||
}
|
||||
|
||||
// PullReviewComment represents a comment on a pull request review
|
||||
type PullReviewComment struct {
|
||||
ID int64 `json:"id"`
|
||||
Body string `json:"body"`
|
||||
Reviewer *User `json:"user"`
|
||||
ReviewID int64 `json:"pull_request_review_id"`
|
||||
Resolver *User `json:"resolver"`
|
||||
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
|
||||
Path string `json:"path"`
|
||||
CommitID string `json:"commit_id"`
|
||||
OrigCommitID string `json:"original_commit_id"`
|
||||
DiffHunk string `json:"diff_hunk"`
|
||||
LineNum uint64 `json:"position"`
|
||||
OldLineNum uint64 `json:"original_position"`
|
||||
|
||||
HTMLURL string `json:"html_url"`
|
||||
HTMLPullURL string `json:"pull_request_url"`
|
||||
}
|
||||
|
||||
// CreatePullReviewOptions are options to create a pull review
|
||||
type CreatePullReviewOptions struct {
|
||||
State ReviewStateType `json:"event"`
|
||||
Body string `json:"body"`
|
||||
CommitID string `json:"commit_id"`
|
||||
Comments []CreatePullReviewComment `json:"comments"`
|
||||
}
|
||||
|
||||
// CreatePullReviewComment represent a review comment for creation api
|
||||
type CreatePullReviewComment struct {
|
||||
// the tree path
|
||||
Path string `json:"path"`
|
||||
Body string `json:"body"`
|
||||
// if comment to old file line or 0
|
||||
OldLineNum int64 `json:"old_position"`
|
||||
// if comment to new file line or 0
|
||||
NewLineNum int64 `json:"new_position"`
|
||||
}
|
||||
|
||||
// SubmitPullReviewOptions are options to submit a pending pull review
|
||||
type SubmitPullReviewOptions struct {
|
||||
State ReviewStateType `json:"event"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// DismissPullReviewOptions are options to dismiss a pull review
|
||||
type DismissPullReviewOptions struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PullReviewRequestOptions are options to add or remove pull review requests
|
||||
type PullReviewRequestOptions struct {
|
||||
Reviewers []string `json:"reviewers"`
|
||||
TeamReviewers []string `json:"team_reviewers"`
|
||||
}
|
||||
|
||||
// ListPullReviewsOptions options for listing PullReviews
|
||||
type ListPullReviewsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// Validate the CreatePullReviewOptions struct
|
||||
func (opt CreatePullReviewOptions) Validate() error {
|
||||
if opt.State != ReviewStateApproved && len(strings.TrimSpace(opt.Body)) == 0 {
|
||||
return fmt.Errorf("body is empty")
|
||||
}
|
||||
for i := range opt.Comments {
|
||||
if err := opt.Comments[i].Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the SubmitPullReviewOptions struct
|
||||
func (opt SubmitPullReviewOptions) Validate() error {
|
||||
if opt.State != ReviewStateApproved && len(strings.TrimSpace(opt.Body)) == 0 {
|
||||
return fmt.Errorf("body is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate the CreatePullReviewComment struct
|
||||
func (opt CreatePullReviewComment) Validate() error {
|
||||
if len(strings.TrimSpace(opt.Body)) == 0 {
|
||||
return fmt.Errorf("body is empty")
|
||||
}
|
||||
if opt.NewLineNum != 0 && opt.OldLineNum != 0 {
|
||||
return fmt.Errorf("old and new line num are set, cant identify the code comment position")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPullReviews lists all reviews of a pull request
|
||||
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
rs := make([]*PullReview, 0, opt.PageSize)
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index))
|
||||
link.RawQuery = opt.ListOptions.getURLQuery().Encode()
|
||||
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rs)
|
||||
return rs, resp, err
|
||||
}
|
||||
|
||||
// GetPullReview gets a specific review of a pull request
|
||||
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r := new(PullReview)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil, &r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// ListPullReviewComments lists all comments of a pull request review
|
||||
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rcl := make([]*PullReviewComment, 0, 4)
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/comments", owner, repo, index, id))
|
||||
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rcl)
|
||||
return rcl, resp, err
|
||||
}
|
||||
|
||||
// DeletePullReview delete a specific review from a pull request
|
||||
func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// CreatePullReview create a review to an pull request
|
||||
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r := new(PullReview)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body), r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// SubmitPullReview submit a pending review to an pull request
|
||||
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r := new(PullReview)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id),
|
||||
jsonHeader, bytes.NewReader(body), r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// CreateReviewRequests create review requests to an pull request
|
||||
func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteReviewRequests delete review requests to an pull request
|
||||
func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
|
||||
jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DismissPullReview dismiss a review for a pull request
|
||||
func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt DismissPullReviewOptions) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/dismissals", owner, repo, index, id),
|
||||
jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// UnDismissPullReview cancel to dismiss a review for a pull request
|
||||
func (c *Client) UnDismissPullReview(owner, repo string, index, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/undismissals", owner, repo, index, id),
|
||||
jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
202
vendor/code.gitea.io/sdk/gitea/release.go
generated
vendored
202
vendor/code.gitea.io/sdk/gitea/release.go
generated
vendored
|
@ -1,202 +0,0 @@
|
|||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Release represents a repository release
|
||||
type Release struct {
|
||||
ID int64 `json:"id"`
|
||||
TagName string `json:"tag_name"`
|
||||
Target string `json:"target_commitish"`
|
||||
Title string `json:"name"`
|
||||
Note string `json:"body"`
|
||||
URL string `json:"url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
TarURL string `json:"tarball_url"`
|
||||
ZipURL string `json:"zipball_url"`
|
||||
IsDraft bool `json:"draft"`
|
||||
IsPrerelease bool `json:"prerelease"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
Publisher *User `json:"author"`
|
||||
Attachments []*Attachment `json:"assets"`
|
||||
}
|
||||
|
||||
// ListReleasesOptions options for listing repository's releases
|
||||
type ListReleasesOptions struct {
|
||||
ListOptions
|
||||
IsDraft *bool
|
||||
IsPreRelease *bool
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListReleasesOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
|
||||
if opt.IsDraft != nil {
|
||||
query.Add("draft", fmt.Sprintf("%t", *opt.IsDraft))
|
||||
}
|
||||
if opt.IsPreRelease != nil {
|
||||
query.Add("draft", fmt.Sprintf("%t", *opt.IsPreRelease))
|
||||
}
|
||||
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListReleases list releases of a repository
|
||||
func (c *Client) ListReleases(owner, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
releases := make([]*Release, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/releases?%s", owner, repo, opt.QueryEncode()),
|
||||
nil, nil, &releases)
|
||||
return releases, resp, err
|
||||
}
|
||||
|
||||
// GetRelease get a release of a repository by id
|
||||
func (c *Client) GetRelease(owner, repo string, id int64) (*Release, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
r := new(Release)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
|
||||
jsonHeader, nil, &r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// GetReleaseByTag get a release of a repository by tag
|
||||
func (c *Client) GetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||
return c.fallbackGetReleaseByTag(owner, repo, tag)
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &tag); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
r := new(Release)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", owner, repo, tag),
|
||||
nil, nil, &r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// CreateReleaseOption options when creating a release
|
||||
type CreateReleaseOption struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Target string `json:"target_commitish"`
|
||||
Title string `json:"name"`
|
||||
Note string `json:"body"`
|
||||
IsDraft bool `json:"draft"`
|
||||
IsPrerelease bool `json:"prerelease"`
|
||||
}
|
||||
|
||||
// Validate the CreateReleaseOption struct
|
||||
func (opt CreateReleaseOption) Validate() error {
|
||||
if len(strings.TrimSpace(opt.Title)) == 0 {
|
||||
return fmt.Errorf("title is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRelease create a release
|
||||
func (c *Client) CreateRelease(owner, repo string, opt CreateReleaseOption) (*Release, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
r := new(Release)
|
||||
resp, err := c.getParsedResponse("POST",
|
||||
fmt.Sprintf("/repos/%s/%s/releases", owner, repo),
|
||||
jsonHeader, bytes.NewReader(body), r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// EditReleaseOption options when editing a release
|
||||
type EditReleaseOption struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Target string `json:"target_commitish"`
|
||||
Title string `json:"name"`
|
||||
Note string `json:"body"`
|
||||
IsDraft *bool `json:"draft"`
|
||||
IsPrerelease *bool `json:"prerelease"`
|
||||
}
|
||||
|
||||
// EditRelease edit a release
|
||||
func (c *Client) EditRelease(owner, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(form)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
r := new(Release)
|
||||
resp, err := c.getParsedResponse("PATCH",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
|
||||
jsonHeader, bytes.NewReader(body), r)
|
||||
return r, resp, err
|
||||
}
|
||||
|
||||
// DeleteRelease delete a release from a repository, keeping its tag
|
||||
func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id),
|
||||
nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteReleaseByTag deletes a release frm a repository by tag
|
||||
func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", user, repo, tag),
|
||||
nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 )
|
||||
func (c *Client) fallbackGetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
|
||||
for i := 1; ; i++ {
|
||||
rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions: ListOptions{Page: i}})
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
if len(rl) == 0 {
|
||||
return nil,
|
||||
&Response{&http.Response{StatusCode: 404}},
|
||||
fmt.Errorf("release with tag '%s' not found", tag)
|
||||
}
|
||||
for _, r := range rl {
|
||||
if r.TagName == tag {
|
||||
return r, resp, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
534
vendor/code.gitea.io/sdk/gitea/repo.go
generated
vendored
534
vendor/code.gitea.io/sdk/gitea/repo.go
generated
vendored
|
@ -1,534 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Permission represents a set of permissions
|
||||
type Permission struct {
|
||||
Admin bool `json:"admin"`
|
||||
Push bool `json:"push"`
|
||||
Pull bool `json:"pull"`
|
||||
}
|
||||
|
||||
// InternalTracker represents settings for internal tracker
|
||||
type InternalTracker struct {
|
||||
// Enable time tracking (Built-in issue tracker)
|
||||
EnableTimeTracker bool `json:"enable_time_tracker"`
|
||||
// Let only contributors track time (Built-in issue tracker)
|
||||
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
|
||||
// Enable dependencies for issues and pull requests (Built-in issue tracker)
|
||||
EnableIssueDependencies bool `json:"enable_issue_dependencies"`
|
||||
}
|
||||
|
||||
// ExternalTracker represents settings for external tracker
|
||||
type ExternalTracker struct {
|
||||
// URL of external issue tracker.
|
||||
ExternalTrackerURL string `json:"external_tracker_url"`
|
||||
// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
|
||||
ExternalTrackerFormat string `json:"external_tracker_format"`
|
||||
// External Issue Tracker Number Format, either `numeric` or `alphanumeric`
|
||||
ExternalTrackerStyle string `json:"external_tracker_style"`
|
||||
}
|
||||
|
||||
// ExternalWiki represents setting for external wiki
|
||||
type ExternalWiki struct {
|
||||
// URL of external wiki.
|
||||
ExternalWikiURL string `json:"external_wiki_url"`
|
||||
}
|
||||
|
||||
// Repository represents a repository
|
||||
type Repository struct {
|
||||
ID int64 `json:"id"`
|
||||
Owner *User `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
FullName string `json:"full_name"`
|
||||
Description string `json:"description"`
|
||||
Empty bool `json:"empty"`
|
||||
Private bool `json:"private"`
|
||||
Fork bool `json:"fork"`
|
||||
Template bool `json:"template"`
|
||||
Parent *Repository `json:"parent"`
|
||||
Mirror bool `json:"mirror"`
|
||||
Size int `json:"size"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
SSHURL string `json:"ssh_url"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
OriginalURL string `json:"original_url"`
|
||||
Website string `json:"website"`
|
||||
Stars int `json:"stars_count"`
|
||||
Forks int `json:"forks_count"`
|
||||
Watchers int `json:"watchers_count"`
|
||||
OpenIssues int `json:"open_issues_count"`
|
||||
OpenPulls int `json:"open_pr_counter"`
|
||||
Releases int `json:"release_counter"`
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
Archived bool `json:"archived"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
Permissions *Permission `json:"permissions,omitempty"`
|
||||
HasIssues bool `json:"has_issues"`
|
||||
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
|
||||
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
|
||||
HasWiki bool `json:"has_wiki"`
|
||||
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||
HasPullRequests bool `json:"has_pull_requests"`
|
||||
HasProjects bool `json:"has_projects"`
|
||||
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
|
||||
AllowMerge bool `json:"allow_merge_commits"`
|
||||
AllowRebase bool `json:"allow_rebase"`
|
||||
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
|
||||
AllowSquash bool `json:"allow_squash_merge"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Internal bool `json:"internal"`
|
||||
MirrorInterval string `json:"mirror_interval"`
|
||||
DefaultMergeStyle MergeStyle `json:"default_merge_style"`
|
||||
}
|
||||
|
||||
// RepoType represent repo type
|
||||
type RepoType string
|
||||
|
||||
const (
|
||||
// RepoTypeNone dont specify a type
|
||||
RepoTypeNone RepoType = ""
|
||||
// RepoTypeSource is the default repo type
|
||||
RepoTypeSource RepoType = "source"
|
||||
// RepoTypeFork is a repo witch was forked from an other one
|
||||
RepoTypeFork RepoType = "fork"
|
||||
// RepoTypeMirror represents an mirror repo
|
||||
RepoTypeMirror RepoType = "mirror"
|
||||
)
|
||||
|
||||
// TrustModel represent how git signatures are handled in a repository
|
||||
type TrustModel string
|
||||
|
||||
const (
|
||||
// TrustModelDefault use TM set by global config
|
||||
TrustModelDefault TrustModel = "default"
|
||||
// TrustModelCollaborator gpg signature has to be owned by a repo collaborator
|
||||
TrustModelCollaborator TrustModel = "collaborator"
|
||||
// TrustModelCommitter gpg signature has to match committer
|
||||
TrustModelCommitter TrustModel = "committer"
|
||||
// TrustModelCollaboratorCommitter gpg signature has to match committer and owned by a repo collaborator
|
||||
TrustModelCollaboratorCommitter TrustModel = "collaboratorcommitter"
|
||||
)
|
||||
|
||||
// ListReposOptions options for listing repositories
|
||||
type ListReposOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListMyRepos lists all repositories for the authenticated user that has access to.
|
||||
func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, *Response, error) {
|
||||
opt.setDefaults()
|
||||
repos := make([]*Repository, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/repos?%s", opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// ListUserRepos list all repositories of one user by user's name
|
||||
func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
repos := make([]*Repository, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// ListOrgReposOptions options for a organization's repositories
|
||||
type ListOrgReposOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListOrgRepos list all repositories of one organization by organization's name
|
||||
func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
repos := make([]*Repository, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// SearchRepoOptions options for searching repositories
|
||||
type SearchRepoOptions struct {
|
||||
ListOptions
|
||||
|
||||
// The keyword to query
|
||||
Keyword string
|
||||
// Limit search to repositories with keyword as topic
|
||||
KeywordIsTopic bool
|
||||
// Include search of keyword within repository description
|
||||
KeywordInDescription bool
|
||||
|
||||
/*
|
||||
User Filter
|
||||
*/
|
||||
|
||||
// Repo Owner
|
||||
OwnerID int64
|
||||
// Stared By UserID
|
||||
StarredByUserID int64
|
||||
|
||||
/*
|
||||
Repo Attributes
|
||||
*/
|
||||
|
||||
// pubic, private or all repositories (defaults to all)
|
||||
IsPrivate *bool
|
||||
// archived, non-archived or all repositories (defaults to all)
|
||||
IsArchived *bool
|
||||
// Exclude template repos from search
|
||||
ExcludeTemplate bool
|
||||
// Filter by "fork", "source", "mirror"
|
||||
Type RepoType
|
||||
|
||||
/*
|
||||
Sort Filters
|
||||
*/
|
||||
|
||||
// sort repos by attribute. Supported values are "alpha", "created", "updated", "size", and "id". Default is "alpha"
|
||||
Sort string
|
||||
// sort order, either "asc" (ascending) or "desc" (descending). Default is "asc", ignored if "sort" is not specified.
|
||||
Order string
|
||||
// Repo owner to prioritize in the results
|
||||
PrioritizedByOwnerID int64
|
||||
|
||||
/*
|
||||
Cover EdgeCases
|
||||
*/
|
||||
// if set all other options are ignored and this string is used as query
|
||||
RawQuery string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *SearchRepoOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if opt.Keyword != "" {
|
||||
query.Add("q", opt.Keyword)
|
||||
}
|
||||
if opt.KeywordIsTopic {
|
||||
query.Add("topic", "true")
|
||||
}
|
||||
if opt.KeywordInDescription {
|
||||
query.Add("includeDesc", "true")
|
||||
}
|
||||
|
||||
// User Filter
|
||||
if opt.OwnerID > 0 {
|
||||
query.Add("uid", fmt.Sprintf("%d", opt.OwnerID))
|
||||
query.Add("exclusive", "true")
|
||||
}
|
||||
if opt.StarredByUserID > 0 {
|
||||
query.Add("starredBy", fmt.Sprintf("%d", opt.StarredByUserID))
|
||||
}
|
||||
|
||||
// Repo Attributes
|
||||
if opt.IsPrivate != nil {
|
||||
query.Add("is_private", fmt.Sprintf("%v", opt.IsPrivate))
|
||||
}
|
||||
if opt.IsArchived != nil {
|
||||
query.Add("archived", fmt.Sprintf("%v", opt.IsArchived))
|
||||
}
|
||||
if opt.ExcludeTemplate {
|
||||
query.Add("template", "false")
|
||||
}
|
||||
if len(opt.Type) != 0 {
|
||||
query.Add("mode", string(opt.Type))
|
||||
}
|
||||
|
||||
// Sort Filters
|
||||
if opt.Sort != "" {
|
||||
query.Add("sort", opt.Sort)
|
||||
}
|
||||
if opt.PrioritizedByOwnerID > 0 {
|
||||
query.Add("priority_owner_id", fmt.Sprintf("%d", opt.PrioritizedByOwnerID))
|
||||
}
|
||||
if opt.Order != "" {
|
||||
query.Add("order", opt.Order)
|
||||
}
|
||||
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
type searchRepoResponse struct {
|
||||
Repos []*Repository `json:"data"`
|
||||
}
|
||||
|
||||
// SearchRepos searches for repositories matching the given filters
|
||||
func (c *Client) SearchRepos(opt SearchRepoOptions) ([]*Repository, *Response, error) {
|
||||
opt.setDefaults()
|
||||
repos := new(searchRepoResponse)
|
||||
|
||||
link, _ := url.Parse("/repos/search")
|
||||
|
||||
if len(opt.RawQuery) != 0 {
|
||||
link.RawQuery = opt.RawQuery
|
||||
} else {
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
// IsPrivate only works on gitea >= 1.12.0
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil && opt.IsPrivate != nil {
|
||||
if *opt.IsPrivate {
|
||||
// private repos only not supported on gitea <= 1.11.x
|
||||
return nil, nil, err
|
||||
}
|
||||
link.Query().Add("private", "false")
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &repos)
|
||||
return repos.Repos, resp, err
|
||||
}
|
||||
|
||||
// CreateRepoOption options when creating repository
|
||||
type CreateRepoOption struct {
|
||||
// Name of the repository to create
|
||||
Name string `json:"name"`
|
||||
// Description of the repository to create
|
||||
Description string `json:"description"`
|
||||
// Whether the repository is private
|
||||
Private bool `json:"private"`
|
||||
// Issue Label set to use
|
||||
IssueLabels string `json:"issue_labels"`
|
||||
// Whether the repository should be auto-intialized?
|
||||
AutoInit bool `json:"auto_init"`
|
||||
// Whether the repository is template
|
||||
Template bool `json:"template"`
|
||||
// Gitignores to use
|
||||
Gitignores string `json:"gitignores"`
|
||||
// License to use
|
||||
License string `json:"license"`
|
||||
// Readme of the repository to create
|
||||
Readme string `json:"readme"`
|
||||
// DefaultBranch of the repository (used when initializes and in template)
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
// TrustModel of the repository
|
||||
TrustModel TrustModel `json:"trust_model"`
|
||||
}
|
||||
|
||||
// Validate the CreateRepoOption struct
|
||||
func (opt CreateRepoOption) Validate(c *Client) error {
|
||||
if len(strings.TrimSpace(opt.Name)) == 0 {
|
||||
return fmt.Errorf("name is empty")
|
||||
}
|
||||
if len(opt.Name) > 100 {
|
||||
return fmt.Errorf("name has more than 100 chars")
|
||||
}
|
||||
if len(opt.Description) > 255 {
|
||||
return fmt.Errorf("name has more than 255 chars")
|
||||
}
|
||||
if len(opt.DefaultBranch) > 100 {
|
||||
return fmt.Errorf("name has more than 100 chars")
|
||||
}
|
||||
if len(opt.TrustModel) != 0 {
|
||||
if err := c.CheckServerVersionConstraint(">=1.13.0"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRepo creates a repository for authenticated user.
|
||||
func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, *Response, error) {
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
||||
|
||||
// CreateOrgRepo creates an organization repository for authenticated user.
|
||||
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&org); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
||||
|
||||
// GetRepo returns information of a repository of given owner.
|
||||
func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
|
||||
return repo, resp, err
|
||||
}
|
||||
|
||||
// GetRepoByID returns information of a repository by a giver repository ID.
|
||||
func (c *Client) GetRepoByID(id int64) (*Repository, *Response, error) {
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repositories/%d", id), nil, nil, repo)
|
||||
return repo, resp, err
|
||||
}
|
||||
|
||||
// EditRepoOption options when editing a repository's properties
|
||||
type EditRepoOption struct {
|
||||
// name of the repository
|
||||
Name *string `json:"name,omitempty"`
|
||||
// a short description of the repository.
|
||||
Description *string `json:"description,omitempty"`
|
||||
// a URL with more information about the repository.
|
||||
Website *string `json:"website,omitempty"`
|
||||
// either `true` to make the repository private or `false` to make it public.
|
||||
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
|
||||
// owners and a non-owner tries to change the value of private.
|
||||
Private *bool `json:"private,omitempty"`
|
||||
// either `true` to make this repository a template or `false` to make it a normal repository
|
||||
Template *bool `json:"template,omitempty"`
|
||||
// either `true` to enable issues for this repository or `false` to disable them.
|
||||
HasIssues *bool `json:"has_issues,omitempty"`
|
||||
// set this structure to configure internal issue tracker (requires has_issues)
|
||||
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
|
||||
// set this structure to use external issue tracker (requires has_issues)
|
||||
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
|
||||
// either `true` to enable the wiki for this repository or `false` to disable it.
|
||||
HasWiki *bool `json:"has_wiki,omitempty"`
|
||||
// set this structure to use external wiki instead of internal (requires has_wiki)
|
||||
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||
// sets the default branch for this repository.
|
||||
DefaultBranch *string `json:"default_branch,omitempty"`
|
||||
// either `true` to allow pull requests, or `false` to prevent pull request.
|
||||
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
||||
// either `true` to enable project unit, or `false` to disable them.
|
||||
HasProjects *bool `json:"has_projects,omitempty"`
|
||||
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
|
||||
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
|
||||
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
|
||||
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
|
||||
// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
|
||||
AllowRebase *bool `json:"allow_rebase,omitempty"`
|
||||
// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
|
||||
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
||||
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
|
||||
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||
// set to `true` to archive this repository.
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
// set to a string like `8h30m0s` to set the mirror interval time
|
||||
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
||||
// either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
|
||||
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
|
||||
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
|
||||
AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
|
||||
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
|
||||
DefaultMergeStyle *MergeStyle `json:"default_merge_style,omitempty"`
|
||||
// set to `true` to archive this repository.
|
||||
}
|
||||
|
||||
// EditRepo edit the properties of a repository
|
||||
func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
||||
|
||||
// DeleteRepo deletes a repository of user or organization.
|
||||
func (c *Client) DeleteRepo(owner, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// MirrorSync adds a mirrored repository to the mirror sync queue.
|
||||
func (c *Client) MirrorSync(owner, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetRepoLanguages return language stats of a repo
|
||||
func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
langMap := make(map[string]int64)
|
||||
|
||||
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
if err = json.Unmarshal(data, &langMap); err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return langMap, resp, nil
|
||||
}
|
||||
|
||||
// ArchiveType represent supported archive formats by gitea
|
||||
type ArchiveType string
|
||||
|
||||
const (
|
||||
// ZipArchive represent zip format
|
||||
ZipArchive ArchiveType = ".zip"
|
||||
// TarGZArchive represent tar.gz format
|
||||
TarGZArchive ArchiveType = ".tar.gz"
|
||||
)
|
||||
|
||||
// GetArchive get an archive of a repository by git reference
|
||||
// e.g.: ref -> master, 70b7c74b33, v1.2.1, ...
|
||||
func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ref = pathEscapeSegments(ref)
|
||||
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
|
||||
}
|
||||
|
||||
// GetArchiveReader gets a `git archive` for a particular tree-ish git reference
|
||||
// such as a branch name (`master`), a commit hash (`70b7c74b33`), a tag
|
||||
// (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is
|
||||
// the responsibility of the client to close the reader.
|
||||
func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ref = pathEscapeSegments(ref)
|
||||
resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
if _, err := statusCodeToErr(resp); err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return resp.Body, resp, nil
|
||||
}
|
143
vendor/code.gitea.io/sdk/gitea/repo_branch.go
generated
vendored
143
vendor/code.gitea.io/sdk/gitea/repo_branch.go
generated
vendored
|
@ -1,143 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PayloadUser represents the author or committer of a commit
|
||||
type PayloadUser struct {
|
||||
// Full name of the commit author
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
// PayloadCommit represents a commit
|
||||
type PayloadCommit struct {
|
||||
// sha1 hash of the commit
|
||||
ID string `json:"id"`
|
||||
Message string `json:"message"`
|
||||
URL string `json:"url"`
|
||||
Author *PayloadUser `json:"author"`
|
||||
Committer *PayloadUser `json:"committer"`
|
||||
Verification *PayloadCommitVerification `json:"verification"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Added []string `json:"added"`
|
||||
Removed []string `json:"removed"`
|
||||
Modified []string `json:"modified"`
|
||||
}
|
||||
|
||||
// PayloadCommitVerification represents the GPG verification of a commit
|
||||
type PayloadCommitVerification struct {
|
||||
Verified bool `json:"verified"`
|
||||
Reason string `json:"reason"`
|
||||
Signature string `json:"signature"`
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
// Branch represents a repository branch
|
||||
type Branch struct {
|
||||
Name string `json:"name"`
|
||||
Commit *PayloadCommit `json:"commit"`
|
||||
Protected bool `json:"protected"`
|
||||
RequiredApprovals int64 `json:"required_approvals"`
|
||||
EnableStatusCheck bool `json:"enable_status_check"`
|
||||
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||
UserCanPush bool `json:"user_can_push"`
|
||||
UserCanMerge bool `json:"user_can_merge"`
|
||||
EffectiveBranchProtectionName string `json:"effective_branch_protection_name"`
|
||||
}
|
||||
|
||||
// ListRepoBranchesOptions options for listing a repository's branches
|
||||
type ListRepoBranchesOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoBranches list all the branches of one repository
|
||||
func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
branches := make([]*Branch, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches)
|
||||
return branches, resp, err
|
||||
}
|
||||
|
||||
// GetRepoBranch get one branch's information of one repository
|
||||
func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
b := new(Branch)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return b, resp, nil
|
||||
}
|
||||
|
||||
// DeleteRepoBranch delete a branch in a repository
|
||||
func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil)
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
return status == 204, resp, nil
|
||||
}
|
||||
|
||||
// CreateBranchOption options when creating a branch in a repository
|
||||
type CreateBranchOption struct {
|
||||
// Name of the branch to create
|
||||
BranchName string `json:"new_branch_name"`
|
||||
// Name of the old branch to create from (optional)
|
||||
OldBranchName string `json:"old_branch_name"`
|
||||
}
|
||||
|
||||
// Validate the CreateBranchOption struct
|
||||
func (opt CreateBranchOption) Validate() error {
|
||||
if len(opt.BranchName) == 0 {
|
||||
return fmt.Errorf("BranchName is empty")
|
||||
}
|
||||
if len(opt.BranchName) > 100 {
|
||||
return fmt.Errorf("BranchName to long")
|
||||
}
|
||||
if len(opt.OldBranchName) > 100 {
|
||||
return fmt.Errorf("OldBranchName to long")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateBranch creates a branch for a user's repository
|
||||
func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
branch := new(Branch)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/branches", owner, repo), jsonHeader, bytes.NewReader(body), branch)
|
||||
return branch, resp, err
|
||||
}
|
168
vendor/code.gitea.io/sdk/gitea/repo_branch_protection.go
generated
vendored
168
vendor/code.gitea.io/sdk/gitea/repo_branch_protection.go
generated
vendored
|
@ -1,168 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BranchProtection represents a branch protection for a repository
|
||||
type BranchProtection struct {
|
||||
BranchName string `json:"branch_name"`
|
||||
EnablePush bool `json:"enable_push"`
|
||||
EnablePushWhitelist bool `json:"enable_push_whitelist"`
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
EnableStatusCheck bool `json:"enable_status_check"`
|
||||
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||
RequiredApprovals int64 `json:"required_approvals"`
|
||||
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
|
||||
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
|
||||
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// CreateBranchProtectionOption options for creating a branch protection
|
||||
type CreateBranchProtectionOption struct {
|
||||
BranchName string `json:"branch_name"`
|
||||
EnablePush bool `json:"enable_push"`
|
||||
EnablePushWhitelist bool `json:"enable_push_whitelist"`
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
EnableStatusCheck bool `json:"enable_status_check"`
|
||||
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||
RequiredApprovals int64 `json:"required_approvals"`
|
||||
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
|
||||
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
|
||||
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||
}
|
||||
|
||||
// EditBranchProtectionOption options for editing a branch protection
|
||||
type EditBranchProtectionOption struct {
|
||||
EnablePush *bool `json:"enable_push"`
|
||||
EnablePushWhitelist *bool `json:"enable_push_whitelist"`
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys *bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
EnableStatusCheck *bool `json:"enable_status_check"`
|
||||
StatusCheckContexts []string `json:"status_check_contexts"`
|
||||
RequiredApprovals *int64 `json:"required_approvals"`
|
||||
EnableApprovalsWhitelist *bool `json:"enable_approvals_whitelist"`
|
||||
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
|
||||
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
|
||||
BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"`
|
||||
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
|
||||
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
|
||||
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
|
||||
RequireSignedCommits *bool `json:"require_signed_commits"`
|
||||
ProtectedFilePatterns *string `json:"protected_file_patterns"`
|
||||
}
|
||||
|
||||
// ListBranchProtectionsOptions list branch protection options
|
||||
type ListBranchProtectionsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListBranchProtections list branch protections for a repo
|
||||
func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bps := make([]*BranchProtection, 0, opt.PageSize)
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo))
|
||||
link.RawQuery = opt.getURLQuery().Encode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &bps)
|
||||
return bps, resp, err
|
||||
}
|
||||
|
||||
// GetBranchProtection gets a branch protection
|
||||
func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bp := new(BranchProtection)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil, bp)
|
||||
return bp, resp, err
|
||||
}
|
||||
|
||||
// CreateBranchProtection creates a branch protection for a repo
|
||||
func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bp := new(BranchProtection)
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo), jsonHeader, bytes.NewReader(body), bp)
|
||||
return bp, resp, err
|
||||
}
|
||||
|
||||
// EditBranchProtection edits a branch protection for a repo
|
||||
func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bp := new(BranchProtection)
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, bytes.NewReader(body), bp)
|
||||
return bp, resp, err
|
||||
}
|
||||
|
||||
// DeleteBranchProtection deletes a branch protection for a repo
|
||||
func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
136
vendor/code.gitea.io/sdk/gitea/repo_collaborator.go
generated
vendored
136
vendor/code.gitea.io/sdk/gitea/repo_collaborator.go
generated
vendored
|
@ -1,136 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ListCollaboratorsOptions options for listing a repository's collaborators
|
||||
type ListCollaboratorsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListCollaborators list a repository's collaborators
|
||||
func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
collaborators := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET",
|
||||
fmt.Sprintf("/repos/%s/%s/collaborators?%s", user, repo, opt.getURLQuery().Encode()),
|
||||
nil, nil, &collaborators)
|
||||
return collaborators, resp, err
|
||||
}
|
||||
|
||||
// IsCollaborator check if a user is a collaborator of a repository
|
||||
func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
if status == 204 {
|
||||
return true, resp, nil
|
||||
}
|
||||
return false, resp, nil
|
||||
}
|
||||
|
||||
// AddCollaboratorOption options when adding a user as a collaborator of a repository
|
||||
type AddCollaboratorOption struct {
|
||||
Permission *AccessMode `json:"permission"`
|
||||
}
|
||||
|
||||
// AccessMode represent the grade of access you have to something
|
||||
type AccessMode string
|
||||
|
||||
const (
|
||||
// AccessModeNone no access
|
||||
AccessModeNone AccessMode = "none"
|
||||
// AccessModeRead read access
|
||||
AccessModeRead AccessMode = "read"
|
||||
// AccessModeWrite write access
|
||||
AccessModeWrite AccessMode = "write"
|
||||
// AccessModeAdmin admin access
|
||||
AccessModeAdmin AccessMode = "admin"
|
||||
// AccessModeOwner owner
|
||||
AccessModeOwner AccessMode = "owner"
|
||||
)
|
||||
|
||||
// Validate the AddCollaboratorOption struct
|
||||
func (opt AddCollaboratorOption) Validate() error {
|
||||
if opt.Permission != nil {
|
||||
if *opt.Permission == AccessModeOwner {
|
||||
*opt.Permission = AccessModeAdmin
|
||||
return nil
|
||||
}
|
||||
if *opt.Permission == AccessModeNone {
|
||||
opt.Permission = nil
|
||||
return nil
|
||||
}
|
||||
if *opt.Permission != AccessModeRead && *opt.Permission != AccessModeWrite && *opt.Permission != AccessModeAdmin {
|
||||
return fmt.Errorf("permission mode invalid")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCollaborator add some user as a collaborator of a repository
|
||||
func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteCollaborator remove a collaborator from a repository
|
||||
func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetReviewers return all users that can be requested to review in this repo
|
||||
func (c *Client) GetReviewers(user, repo string) ([]*User, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reviewers := make([]*User, 0, 5)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/reviewers", user, repo), nil, nil, &reviewers)
|
||||
return reviewers, resp, err
|
||||
}
|
||||
|
||||
// GetAssignees return all users that have write access and can be assigned to issues
|
||||
func (c *Client) GetAssignees(user, repo string) ([]*User, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
assignees := make([]*User, 0, 5)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/assignees", user, repo), nil, nil, &assignees)
|
||||
return assignees, resp, err
|
||||
}
|
101
vendor/code.gitea.io/sdk/gitea/repo_commit.go
generated
vendored
101
vendor/code.gitea.io/sdk/gitea/repo_commit.go
generated
vendored
|
@ -1,101 +0,0 @@
|
|||
// Copyright 2018 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Identity for a person's identity like an author or committer
|
||||
type Identity struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// CommitMeta contains meta information of a commit in terms of API.
|
||||
type CommitMeta struct {
|
||||
URL string `json:"url"`
|
||||
SHA string `json:"sha"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// CommitUser contains information of a user in the context of a commit.
|
||||
type CommitUser struct {
|
||||
Identity
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
// RepoCommit contains information of a commit in the context of a repository.
|
||||
type RepoCommit struct {
|
||||
URL string `json:"url"`
|
||||
Author *CommitUser `json:"author"`
|
||||
Committer *CommitUser `json:"committer"`
|
||||
Message string `json:"message"`
|
||||
Tree *CommitMeta `json:"tree"`
|
||||
}
|
||||
|
||||
// Commit contains information generated from a Git commit.
|
||||
type Commit struct {
|
||||
*CommitMeta
|
||||
HTMLURL string `json:"html_url"`
|
||||
RepoCommit *RepoCommit `json:"commit"`
|
||||
Author *User `json:"author"`
|
||||
Committer *User `json:"committer"`
|
||||
Parents []*CommitMeta `json:"parents"`
|
||||
Files []*CommitAffectedFiles `json:"files"`
|
||||
}
|
||||
|
||||
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
|
||||
type CommitDateOptions struct {
|
||||
Author time.Time `json:"author"`
|
||||
Committer time.Time `json:"committer"`
|
||||
}
|
||||
|
||||
// CommitAffectedFiles store information about files affected by the commit
|
||||
type CommitAffectedFiles struct {
|
||||
Filename string `json:"filename"`
|
||||
}
|
||||
|
||||
// GetSingleCommit returns a single commit
|
||||
func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &commitID); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
commit := new(Commit)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit)
|
||||
return commit, resp, err
|
||||
}
|
||||
|
||||
// ListCommitOptions list commit options
|
||||
type ListCommitOptions struct {
|
||||
ListOptions
|
||||
//SHA or branch to start listing commits from (usually 'master')
|
||||
SHA string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListCommitOptions) QueryEncode() string {
|
||||
query := opt.ListOptions.getURLQuery()
|
||||
if opt.SHA != "" {
|
||||
query.Add("sha", opt.SHA)
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListRepoCommits return list of commits from a repo
|
||||
func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo))
|
||||
opt.setDefaults()
|
||||
commits := make([]*Commit, 0, opt.PageSize)
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &commits)
|
||||
return commits, resp, err
|
||||
}
|
247
vendor/code.gitea.io/sdk/gitea/repo_file.go
generated
vendored
247
vendor/code.gitea.io/sdk/gitea/repo_file.go
generated
vendored
|
@ -1,247 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FileOptions options for all file APIs
|
||||
type FileOptions struct {
|
||||
// message (optional) for the commit of this file. if not supplied, a default message will be used
|
||||
Message string `json:"message"`
|
||||
// branch (optional) to base this file from. if not given, the default branch is used
|
||||
BranchName string `json:"branch"`
|
||||
// new_branch (optional) will make a new branch from `branch` before creating the file
|
||||
NewBranchName string `json:"new_branch"`
|
||||
// `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||
Author Identity `json:"author"`
|
||||
Committer Identity `json:"committer"`
|
||||
Dates CommitDateOptions `json:"dates"`
|
||||
// Add a Signed-off-by trailer by the committer at the end of the commit log message.
|
||||
Signoff bool `json:"signoff"`
|
||||
}
|
||||
|
||||
// CreateFileOptions options for creating files
|
||||
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||
type CreateFileOptions struct {
|
||||
FileOptions
|
||||
// content must be base64 encoded
|
||||
// required: true
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// DeleteFileOptions options for deleting files (used for other File structs below)
|
||||
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||
type DeleteFileOptions struct {
|
||||
FileOptions
|
||||
// sha is the SHA for the file that already exists
|
||||
// required: true
|
||||
SHA string `json:"sha"`
|
||||
}
|
||||
|
||||
// UpdateFileOptions options for updating files
|
||||
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
|
||||
type UpdateFileOptions struct {
|
||||
FileOptions
|
||||
// sha is the SHA for the file that already exists
|
||||
// required: true
|
||||
SHA string `json:"sha"`
|
||||
// content must be base64 encoded
|
||||
// required: true
|
||||
Content string `json:"content"`
|
||||
// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
|
||||
FromPath string `json:"from_path"`
|
||||
}
|
||||
|
||||
// FileLinksResponse contains the links for a repo's file
|
||||
type FileLinksResponse struct {
|
||||
Self *string `json:"self"`
|
||||
GitURL *string `json:"git"`
|
||||
HTMLURL *string `json:"html"`
|
||||
}
|
||||
|
||||
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
|
||||
type ContentsResponse struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
SHA string `json:"sha"`
|
||||
// `type` will be `file`, `dir`, `symlink`, or `submodule`
|
||||
Type string `json:"type"`
|
||||
Size int64 `json:"size"`
|
||||
// `encoding` is populated when `type` is `file`, otherwise null
|
||||
Encoding *string `json:"encoding"`
|
||||
// `content` is populated when `type` is `file`, otherwise null
|
||||
Content *string `json:"content"`
|
||||
// `target` is populated when `type` is `symlink`, otherwise null
|
||||
Target *string `json:"target"`
|
||||
URL *string `json:"url"`
|
||||
HTMLURL *string `json:"html_url"`
|
||||
GitURL *string `json:"git_url"`
|
||||
DownloadURL *string `json:"download_url"`
|
||||
// `submodule_git_url` is populated when `type` is `submodule`, otherwise null
|
||||
SubmoduleGitURL *string `json:"submodule_git_url"`
|
||||
Links *FileLinksResponse `json:"_links"`
|
||||
}
|
||||
|
||||
// FileCommitResponse contains information generated from a Git commit for a repo's file.
|
||||
type FileCommitResponse struct {
|
||||
CommitMeta
|
||||
HTMLURL string `json:"html_url"`
|
||||
Author *CommitUser `json:"author"`
|
||||
Committer *CommitUser `json:"committer"`
|
||||
Parents []*CommitMeta `json:"parents"`
|
||||
Message string `json:"message"`
|
||||
Tree *CommitMeta `json:"tree"`
|
||||
}
|
||||
|
||||
// FileResponse contains information about a repo's file
|
||||
type FileResponse struct {
|
||||
Content *ContentsResponse `json:"content"`
|
||||
Commit *FileCommitResponse `json:"commit"`
|
||||
Verification *PayloadCommitVerification `json:"verification"`
|
||||
}
|
||||
|
||||
// FileDeleteResponse contains information about a repo's file that was deleted
|
||||
type FileDeleteResponse struct {
|
||||
Content interface{} `json:"content"` // to be set to nil
|
||||
Commit *FileCommitResponse `json:"commit"`
|
||||
Verification *PayloadCommitVerification `json:"verification"`
|
||||
}
|
||||
|
||||
// GetFile downloads a file of repository, ref can be branch/tag/commit.
|
||||
// e.g.: ref -> master, filepath -> README.md (no leading slash)
|
||||
func (c *Client) GetFile(owner, repo, ref, filepath string) ([]byte, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filepath = pathEscapeSegments(filepath)
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||
ref = pathEscapeSegments(ref)
|
||||
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", owner, repo, ref, filepath), nil, nil)
|
||||
}
|
||||
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), nil, nil)
|
||||
}
|
||||
|
||||
// GetContents get the metadata and contents of a file in a repository
|
||||
// ref is optional
|
||||
func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, *Response, error) {
|
||||
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
cr := new(ContentsResponse)
|
||||
if json.Unmarshal(data, &cr) != nil {
|
||||
return nil, resp, fmt.Errorf("expect file, got directory")
|
||||
}
|
||||
return cr, resp, err
|
||||
}
|
||||
|
||||
// ListContents gets a list of entries in a dir
|
||||
// ref is optional
|
||||
func (c *Client) ListContents(owner, repo, ref, filepath string) ([]*ContentsResponse, *Response, error) {
|
||||
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
crl := make([]*ContentsResponse, 0)
|
||||
if json.Unmarshal(data, &crl) != nil {
|
||||
return nil, resp, fmt.Errorf("expect directory, got file")
|
||||
}
|
||||
return crl, resp, err
|
||||
}
|
||||
|
||||
func (c *Client) getDirOrFileContents(owner, repo, ref, filepath string) ([]byte, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filepath = pathEscapeSegments(strings.TrimPrefix(filepath, "/"))
|
||||
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), jsonHeader, nil)
|
||||
}
|
||||
|
||||
// CreateFile create a file in a repository
|
||||
func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) {
|
||||
var err error
|
||||
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filepath = pathEscapeSegments(filepath)
|
||||
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fr := new(FileResponse)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
|
||||
return fr, resp, err
|
||||
}
|
||||
|
||||
// UpdateFile update a file in a repository
|
||||
func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, *Response, error) {
|
||||
var err error
|
||||
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filepath = pathEscapeSegments(filepath)
|
||||
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fr := new(FileResponse)
|
||||
resp, err := c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
|
||||
return fr, resp, err
|
||||
}
|
||||
|
||||
// DeleteFile delete a file from repository
|
||||
func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) (*Response, error) {
|
||||
var err error
|
||||
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filepath = pathEscapeSegments(filepath)
|
||||
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if status != 200 && status != 204 {
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) setDefaultBranchForOldVersions(owner, repo, branch string) (string, error) {
|
||||
if len(branch) == 0 {
|
||||
// Gitea >= 1.12.0 Use DefaultBranch on "", mimic this for older versions
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_12_0) != nil {
|
||||
r, _, err := c.GetRepo(owner, repo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.DefaultBranch, nil
|
||||
}
|
||||
}
|
||||
return branch, nil
|
||||
}
|
91
vendor/code.gitea.io/sdk/gitea/repo_key.go
generated
vendored
91
vendor/code.gitea.io/sdk/gitea/repo_key.go
generated
vendored
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DeployKey a deploy key
|
||||
type DeployKey struct {
|
||||
ID int64 `json:"id"`
|
||||
KeyID int64 `json:"key_id"`
|
||||
Key string `json:"key"`
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Created time.Time `json:"created_at"`
|
||||
ReadOnly bool `json:"read_only"`
|
||||
Repository *Repository `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// ListDeployKeysOptions options for listing a repository's deploy keys
|
||||
type ListDeployKeysOptions struct {
|
||||
ListOptions
|
||||
KeyID int64
|
||||
Fingerprint string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *ListDeployKeysOptions) QueryEncode() string {
|
||||
query := opt.getURLQuery()
|
||||
if opt.KeyID > 0 {
|
||||
query.Add("key_id", fmt.Sprintf("%d", opt.KeyID))
|
||||
}
|
||||
if len(opt.Fingerprint) > 0 {
|
||||
query.Add("fingerprint", opt.Fingerprint)
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
// ListDeployKeys list all the deploy keys of one repository
|
||||
func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo))
|
||||
opt.setDefaults()
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
keys := make([]*DeployKey, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &keys)
|
||||
return keys, resp, err
|
||||
}
|
||||
|
||||
// GetDeployKey get one deploy key with key id
|
||||
func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key := new(DeployKey)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// CreateDeployKey options when create one deploy key
|
||||
func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key := new(DeployKey)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/keys", user, repo), jsonHeader, bytes.NewReader(body), key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// DeleteDeployKey delete deploy key with key id
|
||||
func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil)
|
||||
return resp, err
|
||||
}
|
132
vendor/code.gitea.io/sdk/gitea/repo_migrate.go
generated
vendored
132
vendor/code.gitea.io/sdk/gitea/repo_migrate.go
generated
vendored
|
@ -1,132 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GitServiceType represents a git service
|
||||
type GitServiceType string
|
||||
|
||||
const (
|
||||
// GitServicePlain represents a plain git service
|
||||
GitServicePlain GitServiceType = "git"
|
||||
//GitServiceGithub represents github.com
|
||||
GitServiceGithub GitServiceType = "github"
|
||||
// GitServiceGitlab represents a gitlab service
|
||||
GitServiceGitlab GitServiceType = "gitlab"
|
||||
// GitServiceGitea represents a gitea service
|
||||
GitServiceGitea GitServiceType = "gitea"
|
||||
// GitServiceGogs represents a gogs service
|
||||
GitServiceGogs GitServiceType = "gogs"
|
||||
)
|
||||
|
||||
// MigrateRepoOption options for migrating a repository from an external service
|
||||
type MigrateRepoOption struct {
|
||||
RepoName string `json:"repo_name"`
|
||||
RepoOwner string `json:"repo_owner"`
|
||||
// deprecated use RepoOwner
|
||||
RepoOwnerID int64 `json:"uid"`
|
||||
CloneAddr string `json:"clone_addr"`
|
||||
Service GitServiceType `json:"service"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
AuthToken string `json:"auth_token"`
|
||||
Mirror bool `json:"mirror"`
|
||||
Private bool `json:"private"`
|
||||
Description string `json:"description"`
|
||||
Wiki bool `json:"wiki"`
|
||||
Milestones bool `json:"milestones"`
|
||||
Labels bool `json:"labels"`
|
||||
Issues bool `json:"issues"`
|
||||
PullRequests bool `json:"pull_requests"`
|
||||
Releases bool `json:"releases"`
|
||||
MirrorInterval string `json:"mirror_interval"`
|
||||
LFS bool `json:"lfs"`
|
||||
LFSEndpoint string `json:"lfs_endpoint"`
|
||||
}
|
||||
|
||||
// Validate the MigrateRepoOption struct
|
||||
func (opt *MigrateRepoOption) Validate(c *Client) error {
|
||||
// check user options
|
||||
if len(opt.CloneAddr) == 0 {
|
||||
return fmt.Errorf("CloneAddr required")
|
||||
}
|
||||
if len(opt.RepoName) == 0 {
|
||||
return fmt.Errorf("RepoName required")
|
||||
} else if len(opt.RepoName) > 100 {
|
||||
return fmt.Errorf("RepoName to long")
|
||||
}
|
||||
if len(opt.Description) > 255 {
|
||||
return fmt.Errorf("Description to long")
|
||||
}
|
||||
switch opt.Service {
|
||||
case GitServiceGithub:
|
||||
if len(opt.AuthToken) == 0 {
|
||||
return fmt.Errorf("github requires token authentication")
|
||||
}
|
||||
case GitServiceGitlab, GitServiceGitea:
|
||||
if len(opt.AuthToken) == 0 {
|
||||
return fmt.Errorf("%s requires token authentication", opt.Service)
|
||||
}
|
||||
// Gitlab is supported since 1.12.0 but api cant handle it until 1.13.0
|
||||
// https://github.com/go-gitea/gitea/pull/12672
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
|
||||
return fmt.Errorf("migrate from service %s need gitea >= 1.13.0", opt.Service)
|
||||
}
|
||||
case GitServiceGogs:
|
||||
if len(opt.AuthToken) == 0 {
|
||||
return fmt.Errorf("gogs requires token authentication")
|
||||
}
|
||||
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
|
||||
return fmt.Errorf("migrate from service gogs need gitea >= 1.14.0")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrateRepo migrates a repository from other Git hosting sources for the authenticated user.
|
||||
//
|
||||
// To migrate a repository for a organization, the authenticated user must be a
|
||||
// owner of the specified organization.
|
||||
func (c *Client) MigrateRepo(opt MigrateRepoOption) (*Repository, *Response, error) {
|
||||
if err := opt.Validate(c); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
if len(opt.AuthToken) != 0 {
|
||||
// gitea <= 1.12 dont understand AuthToken
|
||||
opt.AuthUsername = opt.AuthToken
|
||||
opt.AuthPassword, opt.AuthToken = "", ""
|
||||
}
|
||||
if len(opt.RepoOwner) != 0 {
|
||||
// gitea <= 1.12 dont understand RepoOwner
|
||||
u, _, err := c.GetUserInfo(opt.RepoOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.RepoOwnerID = u.ID
|
||||
} else if opt.RepoOwnerID == 0 {
|
||||
// gitea <= 1.12 require RepoOwnerID
|
||||
u, _, err := c.GetMyUserInfo()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.RepoOwnerID = u.ID
|
||||
}
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", "/repos/migrate", jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
78
vendor/code.gitea.io/sdk/gitea/repo_refs.go
generated
vendored
78
vendor/code.gitea.io/sdk/gitea/repo_refs.go
generated
vendored
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Reference represents a Git reference.
|
||||
type Reference struct {
|
||||
Ref string `json:"ref"`
|
||||
URL string `json:"url"`
|
||||
Object *GitObject `json:"object"`
|
||||
}
|
||||
|
||||
// GitObject represents a Git object.
|
||||
type GitObject struct {
|
||||
Type string `json:"type"`
|
||||
SHA string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// GetRepoRef get one ref's information of one repository
|
||||
func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ref = strings.TrimPrefix(ref, "refs/")
|
||||
ref = pathEscapeSegments(ref)
|
||||
r := new(Reference)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r)
|
||||
if _, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
// Multiple refs
|
||||
return nil, resp, errors.New("no exact match found for this ref")
|
||||
} else if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return r, resp, nil
|
||||
}
|
||||
|
||||
// GetRepoRefs get list of ref's information of one repository
|
||||
func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ref = strings.TrimPrefix(ref, "refs/")
|
||||
ref = pathEscapeSegments(ref)
|
||||
|
||||
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
// Attempt to unmarshal single returned ref.
|
||||
r := new(Reference)
|
||||
refErr := json.Unmarshal(data, r)
|
||||
if refErr == nil {
|
||||
return []*Reference{r}, resp, nil
|
||||
}
|
||||
|
||||
// Attempt to unmarshal multiple refs.
|
||||
var rs []*Reference
|
||||
refsErr := json.Unmarshal(data, &rs)
|
||||
if refsErr == nil {
|
||||
if len(rs) == 0 {
|
||||
return nil, resp, errors.New("unexpected response: an array of refs with length 0")
|
||||
}
|
||||
return rs, resp, nil
|
||||
}
|
||||
|
||||
return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", refErr, refsErr)
|
||||
}
|
96
vendor/code.gitea.io/sdk/gitea/repo_stars.go
generated
vendored
96
vendor/code.gitea.io/sdk/gitea/repo_stars.go
generated
vendored
|
@ -1,96 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ListStargazersOptions options for listing a repository's stargazers
|
||||
type ListStargazersOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoStargazers list a repository's stargazers
|
||||
func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
stargazers := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers)
|
||||
return stargazers, resp, err
|
||||
}
|
||||
|
||||
// GetStarredRepos returns the repos that the given user has starred
|
||||
func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// GetMyStarredRepos returns the repos that the authenticated user has starred
|
||||
func (c *Client) GetMyStarredRepos() ([]*Repository, *Response, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", "/user/starred", jsonHeader, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// IsRepoStarring returns whether the authenticated user has starred the repo or not
|
||||
func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return false, resp, nil
|
||||
case http.StatusNoContent:
|
||||
return true, resp, nil
|
||||
default:
|
||||
return false, resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// StarRepo star specified repo as the authenticated user
|
||||
func (c *Client) StarRepo(user, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNoContent:
|
||||
return resp, nil
|
||||
default:
|
||||
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// UnStarRepo remove star to specified repo as the authenticated user
|
||||
func (c *Client) UnStarRepo(user, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusNoContent:
|
||||
return resp, nil
|
||||
default:
|
||||
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
130
vendor/code.gitea.io/sdk/gitea/repo_tag.go
generated
vendored
130
vendor/code.gitea.io/sdk/gitea/repo_tag.go
generated
vendored
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Tag represents a repository tag
|
||||
type Tag struct {
|
||||
Name string `json:"name"`
|
||||
Message string `json:"message"`
|
||||
ID string `json:"id"`
|
||||
Commit *CommitMeta `json:"commit"`
|
||||
ZipballURL string `json:"zipball_url"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
}
|
||||
|
||||
// AnnotatedTag represents an annotated tag
|
||||
type AnnotatedTag struct {
|
||||
Tag string `json:"tag"`
|
||||
SHA string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
Message string `json:"message"`
|
||||
Tagger *CommitUser `json:"tagger"`
|
||||
Object *AnnotatedTagObject `json:"object"`
|
||||
Verification *PayloadCommitVerification `json:"verification"`
|
||||
}
|
||||
|
||||
// AnnotatedTagObject contains meta information of the tag object
|
||||
type AnnotatedTagObject struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
SHA string `json:"sha"`
|
||||
}
|
||||
|
||||
// ListRepoTagsOptions options for listing a repository's tags
|
||||
type ListRepoTagsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListRepoTags list all the branches of one repository
|
||||
func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
tags := make([]*Tag, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags)
|
||||
return tags, resp, err
|
||||
}
|
||||
|
||||
// GetTag get the tag of a repository
|
||||
func (c *Client) GetTag(user, repo, tag string) (*Tag, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(Tag)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag), nil, nil, &t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// GetAnnotatedTag get the tag object of an annotated tag (not lightweight tags) of a repository
|
||||
func (c *Client) GetAnnotatedTag(user, repo, sha string) (*AnnotatedTag, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(AnnotatedTag)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/tags/%s", user, repo, sha), nil, nil, &t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// CreateTagOption options when creating a tag
|
||||
type CreateTagOption struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Message string `json:"message"`
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
// Validate validates CreateTagOption
|
||||
func (opt CreateTagOption) Validate() error {
|
||||
if len(opt.TagName) == 0 {
|
||||
return fmt.Errorf("TagName is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTag create a new git tag in a repository
|
||||
func (c *Client) CreateTag(user, repo string, opt CreateTagOption) (*Tag, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(Tag)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/tags", user, repo), jsonHeader, bytes.NewReader(body), &t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// DeleteTag deletes a tag from a repository, if no release refers to it
|
||||
func (c *Client) DeleteTag(user, repo, tag string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE",
|
||||
fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag),
|
||||
nil, nil)
|
||||
return resp, err
|
||||
}
|
65
vendor/code.gitea.io/sdk/gitea/repo_team.go
generated
vendored
65
vendor/code.gitea.io/sdk/gitea/repo_team.go
generated
vendored
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetRepoTeams return teams from a repository
|
||||
func (c *Client) GetRepoTeams(user, repo string) ([]*Team, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
teams := make([]*Team, 0, 5)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams", user, repo), nil, nil, &teams)
|
||||
return teams, resp, err
|
||||
}
|
||||
|
||||
// AddRepoTeam add a team to a repository
|
||||
func (c *Client) AddRepoTeam(user, repo, team string) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// RemoveRepoTeam delete a team from a repository
|
||||
func (c *Client) RemoveRepoTeam(user, repo, team string) (*Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// CheckRepoTeam check if team is assigned to repo by name and return it.
|
||||
// If not assigned, it will return nil.
|
||||
func (c *Client) CheckRepoTeam(user, repo, team string) (*Team, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(Team)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil, &t)
|
||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||
// if not found it's not an error, it indicates it's not assigned
|
||||
return nil, resp, nil
|
||||
}
|
||||
return t, resp, err
|
||||
}
|
65
vendor/code.gitea.io/sdk/gitea/repo_template.go
generated
vendored
65
vendor/code.gitea.io/sdk/gitea/repo_template.go
generated
vendored
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CreateRepoFromTemplateOption options when creating repository using a template
|
||||
type CreateRepoFromTemplateOption struct {
|
||||
// Owner is the organization or person who will own the new repository
|
||||
Owner string `json:"owner"`
|
||||
// Name of the repository to create
|
||||
Name string `json:"name"`
|
||||
// Description of the repository to create
|
||||
Description string `json:"description"`
|
||||
// Private is whether the repository is private
|
||||
Private bool `json:"private"`
|
||||
// GitContent include git content of default branch in template repo
|
||||
GitContent bool `json:"git_content"`
|
||||
// Topics include topics of template repo
|
||||
Topics bool `json:"topics"`
|
||||
// GitHooks include git hooks of template repo
|
||||
GitHooks bool `json:"git_hooks"`
|
||||
// Webhooks include webhooks of template repo
|
||||
Webhooks bool `json:"webhooks"`
|
||||
// Avatar include avatar of the template repo
|
||||
Avatar bool `json:"avatar"`
|
||||
// Labels include labels of template repo
|
||||
Labels bool `json:"labels"`
|
||||
}
|
||||
|
||||
// Validate validates CreateRepoFromTemplateOption
|
||||
func (opt CreateRepoFromTemplateOption) Validate() error {
|
||||
if len(opt.Owner) == 0 {
|
||||
return fmt.Errorf("field Owner is required")
|
||||
}
|
||||
if len(opt.Name) == 0 {
|
||||
return fmt.Errorf("field Name is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRepoFromTemplate create a repository using a template
|
||||
func (c *Client) CreateRepoFromTemplate(templateOwner, templateRepo string, opt CreateRepoFromTemplateOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&templateOwner, &templateRepo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := opt.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/generate", templateOwner, templateRepo), jsonHeader, bytes.NewReader(body), &repo)
|
||||
return repo, resp, err
|
||||
}
|
68
vendor/code.gitea.io/sdk/gitea/repo_topics.go
generated
vendored
68
vendor/code.gitea.io/sdk/gitea/repo_topics.go
generated
vendored
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ListRepoTopicsOptions options for listing repo's topics
|
||||
type ListRepoTopicsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// topicsList represents a list of repo's topics
|
||||
type topicsList struct {
|
||||
Topics []string `json:"topics"`
|
||||
}
|
||||
|
||||
// ListRepoTopics list all repository's topics
|
||||
func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
|
||||
list := new(topicsList)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/topics?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, list)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return list.Topics, resp, nil
|
||||
}
|
||||
|
||||
// SetRepoTopics replaces the list of repo's topics
|
||||
func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := topicsList{Topics: list}
|
||||
body, err := json.Marshal(&l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics", user, repo), jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// AddRepoTopic adds a topic to a repo's topics list
|
||||
func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// DeleteRepoTopic deletes a topic from repo's topics list
|
||||
func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
|
||||
return resp, err
|
||||
}
|
36
vendor/code.gitea.io/sdk/gitea/repo_transfer.go
generated
vendored
36
vendor/code.gitea.io/sdk/gitea/repo_transfer.go
generated
vendored
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TransferRepoOption options when transfer a repository's ownership
|
||||
type TransferRepoOption struct {
|
||||
// required: true
|
||||
NewOwner string `json:"new_owner"`
|
||||
// ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.
|
||||
TeamIDs *[]int64 `json:"team_ids"`
|
||||
}
|
||||
|
||||
// TransferRepo transfers the ownership of a repository
|
||||
func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repo := new(Repository)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
|
||||
return repo, resp, err
|
||||
}
|
44
vendor/code.gitea.io/sdk/gitea/repo_tree.go
generated
vendored
44
vendor/code.gitea.io/sdk/gitea/repo_tree.go
generated
vendored
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GitEntry represents a git tree
|
||||
type GitEntry struct {
|
||||
Path string `json:"path"`
|
||||
Mode string `json:"mode"`
|
||||
Type string `json:"type"`
|
||||
Size int64 `json:"size"`
|
||||
SHA string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// GitTreeResponse returns a git tree
|
||||
type GitTreeResponse struct {
|
||||
SHA string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
Entries []GitEntry `json:"tree"`
|
||||
Truncated bool `json:"truncated"`
|
||||
Page int `json:"page"`
|
||||
TotalCount int `json:"total_count"`
|
||||
}
|
||||
|
||||
// GetTrees downloads a file of repository, ref can be branch/tag/commit.
|
||||
// e.g.: ref -> master, tree -> macaron.go(no leading slash)
|
||||
func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user, &repo, &ref); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
trees := new(GitTreeResponse)
|
||||
var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
|
||||
if recursive {
|
||||
path += "?recursive=1"
|
||||
}
|
||||
resp, err := c.getParsedResponse("GET", path, nil, nil, trees)
|
||||
return trees, resp, err
|
||||
}
|
87
vendor/code.gitea.io/sdk/gitea/repo_watch.go
generated
vendored
87
vendor/code.gitea.io/sdk/gitea/repo_watch.go
generated
vendored
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WatchInfo represents an API watch status of one repository
|
||||
type WatchInfo struct {
|
||||
Subscribed bool `json:"subscribed"`
|
||||
Ignored bool `json:"ignored"`
|
||||
Reason interface{} `json:"reason"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
URL string `json:"url"`
|
||||
RepositoryURL string `json:"repository_url"`
|
||||
}
|
||||
|
||||
// GetWatchedRepos list all the watched repos of user
|
||||
func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// GetMyWatchedRepos list repositories watched by the authenticated user
|
||||
func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/subscriptions"), nil, nil, &repos)
|
||||
return repos, resp, err
|
||||
}
|
||||
|
||||
// CheckRepoWatch check if the current user is watching a repo
|
||||
func (c *Client) CheckRepoWatch(owner, repo string) (bool, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||
if err != nil {
|
||||
return false, resp, err
|
||||
}
|
||||
switch status {
|
||||
case http.StatusNotFound:
|
||||
return false, resp, nil
|
||||
case http.StatusOK:
|
||||
return true, resp, nil
|
||||
default:
|
||||
return false, resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
}
|
||||
|
||||
// WatchRepo start to watch a repository
|
||||
func (c *Client) WatchRepo(owner, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if status == http.StatusOK {
|
||||
return resp, nil
|
||||
}
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
||||
|
||||
// UnWatchRepo stop to watch a repository
|
||||
func (c *Client) UnWatchRepo(owner, repo string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if status == http.StatusNoContent {
|
||||
return resp, nil
|
||||
}
|
||||
return resp, fmt.Errorf("unexpected Status: %d", status)
|
||||
}
|
78
vendor/code.gitea.io/sdk/gitea/settings.go
generated
vendored
78
vendor/code.gitea.io/sdk/gitea/settings.go
generated
vendored
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
// GlobalUISettings represent the global ui settings of a gitea instance witch is exposed by API
|
||||
type GlobalUISettings struct {
|
||||
DefaultTheme string `json:"default_theme"`
|
||||
AllowedReactions []string `json:"allowed_reactions"`
|
||||
CustomEmojis []string `json:"custom_emojis"`
|
||||
}
|
||||
|
||||
// GlobalRepoSettings represent the global repository settings of a gitea instance witch is exposed by API
|
||||
type GlobalRepoSettings struct {
|
||||
MirrorsDisabled bool `json:"mirrors_disabled"`
|
||||
HTTPGitDisabled bool `json:"http_git_disabled"`
|
||||
MigrationsDisabled bool `json:"migrations_disabled"`
|
||||
StarsDisabled bool `json:"stars_disabled"`
|
||||
TimeTrackingDisabled bool `json:"time_tracking_disabled"`
|
||||
LFSDisabled bool `json:"lfs_disabled"`
|
||||
}
|
||||
|
||||
// GlobalAPISettings contains global api settings exposed by it
|
||||
type GlobalAPISettings struct {
|
||||
MaxResponseItems int `json:"max_response_items"`
|
||||
DefaultPagingNum int `json:"default_paging_num"`
|
||||
DefaultGitTreesPerPage int `json:"default_git_trees_per_page"`
|
||||
DefaultMaxBlobSize int64 `json:"default_max_blob_size"`
|
||||
}
|
||||
|
||||
// GlobalAttachmentSettings contains global Attachment settings exposed by API
|
||||
type GlobalAttachmentSettings struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
AllowedTypes string `json:"allowed_types"`
|
||||
MaxSize int64 `json:"max_size"`
|
||||
MaxFiles int `json:"max_files"`
|
||||
}
|
||||
|
||||
// GetGlobalUISettings get global ui settings witch are exposed by API
|
||||
func (c *Client) GetGlobalUISettings() (*GlobalUISettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
conf := new(GlobalUISettings)
|
||||
resp, err := c.getParsedResponse("GET", "/settings/ui", jsonHeader, nil, &conf)
|
||||
return conf, resp, err
|
||||
}
|
||||
|
||||
// GetGlobalRepoSettings get global repository settings witch are exposed by API
|
||||
func (c *Client) GetGlobalRepoSettings() (*GlobalRepoSettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
conf := new(GlobalRepoSettings)
|
||||
resp, err := c.getParsedResponse("GET", "/settings/repository", jsonHeader, nil, &conf)
|
||||
return conf, resp, err
|
||||
}
|
||||
|
||||
// GetGlobalAPISettings get global api settings witch are exposed by it
|
||||
func (c *Client) GetGlobalAPISettings() (*GlobalAPISettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
conf := new(GlobalAPISettings)
|
||||
resp, err := c.getParsedResponse("GET", "/settings/api", jsonHeader, nil, &conf)
|
||||
return conf, resp, err
|
||||
}
|
||||
|
||||
// GetGlobalAttachmentSettings get global repository settings witch are exposed by API
|
||||
func (c *Client) GetGlobalAttachmentSettings() (*GlobalAttachmentSettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
conf := new(GlobalAttachmentSettings)
|
||||
resp, err := c.getParsedResponse("GET", "/settings/attachment", jsonHeader, nil, &conf)
|
||||
return conf, resp, err
|
||||
}
|
108
vendor/code.gitea.io/sdk/gitea/status.go
generated
vendored
108
vendor/code.gitea.io/sdk/gitea/status.go
generated
vendored
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatusState holds the state of a Status
|
||||
// It can be "pending", "success", "error", "failure", and "warning"
|
||||
type StatusState string
|
||||
|
||||
const (
|
||||
// StatusPending is for when the Status is Pending
|
||||
StatusPending StatusState = "pending"
|
||||
// StatusSuccess is for when the Status is Success
|
||||
StatusSuccess StatusState = "success"
|
||||
// StatusError is for when the Status is Error
|
||||
StatusError StatusState = "error"
|
||||
// StatusFailure is for when the Status is Failure
|
||||
StatusFailure StatusState = "failure"
|
||||
// StatusWarning is for when the Status is Warning
|
||||
StatusWarning StatusState = "warning"
|
||||
)
|
||||
|
||||
// Status holds a single Status of a single Commit
|
||||
type Status struct {
|
||||
ID int64 `json:"id"`
|
||||
State StatusState `json:"status"`
|
||||
TargetURL string `json:"target_url"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
Context string `json:"context"`
|
||||
Creator *User `json:"creator"`
|
||||
Created time.Time `json:"created_at"`
|
||||
Updated time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// CreateStatusOption holds the information needed to create a new Status for a Commit
|
||||
type CreateStatusOption struct {
|
||||
State StatusState `json:"state"`
|
||||
TargetURL string `json:"target_url"`
|
||||
Description string `json:"description"`
|
||||
Context string `json:"context"`
|
||||
}
|
||||
|
||||
// CreateStatus creates a new Status for a given Commit
|
||||
func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
status := new(Status)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, url.QueryEscape(sha)), jsonHeader, bytes.NewReader(body), status)
|
||||
return status, resp, err
|
||||
}
|
||||
|
||||
// ListStatusesOption options for listing a repository's commit's statuses
|
||||
type ListStatusesOption struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListStatuses returns all statuses for a given Commit by ref
|
||||
func (c *Client) ListStatuses(owner, repo, ref string, opt ListStatusesOption) ([]*Status, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
statuses := make([]*Status, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, ref, opt.getURLQuery().Encode()), jsonHeader, nil, &statuses)
|
||||
return statuses, resp, err
|
||||
}
|
||||
|
||||
// CombinedStatus holds the combined state of several statuses for a single commit
|
||||
type CombinedStatus struct {
|
||||
State StatusState `json:"state"`
|
||||
SHA string `json:"sha"`
|
||||
TotalCount int `json:"total_count"`
|
||||
Statuses []*Status `json:"statuses"`
|
||||
Repository *Repository `json:"repository"`
|
||||
CommitURL string `json:"commit_url"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// GetCombinedStatus returns the CombinedStatus for a given Commit
|
||||
func (c *Client) GetCombinedStatus(owner, repo, ref string) (*CombinedStatus, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
status := new(CombinedStatus)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, ref), jsonHeader, nil, status)
|
||||
|
||||
// gitea api return empty body if nothing here jet
|
||||
if resp != nil && resp.StatusCode == 200 && err != nil {
|
||||
return status, resp, nil
|
||||
}
|
||||
|
||||
return status, resp, err
|
||||
}
|
90
vendor/code.gitea.io/sdk/gitea/user.go
generated
vendored
90
vendor/code.gitea.io/sdk/gitea/user.go
generated
vendored
|
@ -1,90 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// User represents a user
|
||||
type User struct {
|
||||
// the user's id
|
||||
ID int64 `json:"id"`
|
||||
// the user's username
|
||||
UserName string `json:"login"`
|
||||
// the user's full name
|
||||
FullName string `json:"full_name"`
|
||||
Email string `json:"email"`
|
||||
// URL to the user's avatar
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
// User locale
|
||||
Language string `json:"language"`
|
||||
// Is the user an administrator
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
// Date and Time of last login
|
||||
LastLogin time.Time `json:"last_login"`
|
||||
// Date and Time of user creation
|
||||
Created time.Time `json:"created"`
|
||||
// Is user restricted
|
||||
Restricted bool `json:"restricted"`
|
||||
// Is user active
|
||||
IsActive bool `json:"active"`
|
||||
// Is user login prohibited
|
||||
ProhibitLogin bool `json:"prohibit_login"`
|
||||
// the user's location
|
||||
Location string `json:"location"`
|
||||
// the user's website
|
||||
Website string `json:"website"`
|
||||
// the user's description
|
||||
Description string `json:"description"`
|
||||
// User visibility level option
|
||||
Visibility VisibleType `json:"visibility"`
|
||||
|
||||
// user counts
|
||||
FollowerCount int `json:"followers_count"`
|
||||
FollowingCount int `json:"following_count"`
|
||||
StarredRepoCount int `json:"starred_repos_count"`
|
||||
}
|
||||
|
||||
// GetUserInfo get user info by user's name
|
||||
func (c *Client) GetUserInfo(user string) (*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u := new(User)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u)
|
||||
return u, resp, err
|
||||
}
|
||||
|
||||
// GetMyUserInfo get user info of current user
|
||||
func (c *Client) GetMyUserInfo() (*User, *Response, error) {
|
||||
u := new(User)
|
||||
resp, err := c.getParsedResponse("GET", "/user", nil, nil, u)
|
||||
return u, resp, err
|
||||
}
|
||||
|
||||
// GetUserByID returns user by a given user ID
|
||||
func (c *Client) GetUserByID(id int64) (*User, *Response, error) {
|
||||
if id < 0 {
|
||||
return nil, nil, fmt.Errorf("invalid user id %d", id)
|
||||
}
|
||||
|
||||
query := make(url.Values)
|
||||
query.Add("uid", strconv.FormatInt(id, 10))
|
||||
users, resp, err := c.searchUsers(query.Encode())
|
||||
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
if len(users) == 1 {
|
||||
return users[0], resp, err
|
||||
}
|
||||
|
||||
return nil, resp, fmt.Errorf("user not found with id %d", id)
|
||||
}
|
90
vendor/code.gitea.io/sdk/gitea/user_app.go
generated
vendored
90
vendor/code.gitea.io/sdk/gitea/user_app.go
generated
vendored
|
@ -1,90 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// AccessToken represents an API access token.
|
||||
type AccessToken struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Token string `json:"sha1"`
|
||||
TokenLastEight string `json:"token_last_eight"`
|
||||
}
|
||||
|
||||
// ListAccessTokensOptions options for listing a users's access tokens
|
||||
type ListAccessTokensOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListAccessTokens lists all the access tokens of user
|
||||
func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, *Response, error) {
|
||||
c.mutex.RLock()
|
||||
username := c.username
|
||||
c.mutex.RUnlock()
|
||||
if len(username) == 0 {
|
||||
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
opts.setDefaults()
|
||||
tokens := make([]*AccessToken, 0, opts.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", url.PathEscape(username), opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
|
||||
return tokens, resp, err
|
||||
}
|
||||
|
||||
// CreateAccessTokenOption options when create access token
|
||||
type CreateAccessTokenOption struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CreateAccessToken create one access token with options
|
||||
func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *Response, error) {
|
||||
c.mutex.RLock()
|
||||
username := c.username
|
||||
c.mutex.RUnlock()
|
||||
if len(username) == 0 {
|
||||
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
t := new(AccessToken)
|
||||
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", url.PathEscape(username)), jsonHeader, bytes.NewReader(body), t)
|
||||
return t, resp, err
|
||||
}
|
||||
|
||||
// DeleteAccessToken delete token, identified by ID and if not available by name
|
||||
func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
|
||||
c.mutex.RLock()
|
||||
username := c.username
|
||||
c.mutex.RUnlock()
|
||||
if len(username) == 0 {
|
||||
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
|
||||
}
|
||||
|
||||
var token = ""
|
||||
|
||||
switch reflect.ValueOf(value).Kind() {
|
||||
case reflect.Int64:
|
||||
token = fmt.Sprintf("%d", value.(int64))
|
||||
case reflect.String:
|
||||
if err := c.CheckServerVersionConstraint(">= 1.13.0"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token = value.(string)
|
||||
default:
|
||||
return nil, fmt.Errorf("only string and int64 supported")
|
||||
}
|
||||
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", url.PathEscape(username), url.PathEscape(token)), jsonHeader, nil)
|
||||
return resp, err
|
||||
}
|
64
vendor/code.gitea.io/sdk/gitea/user_email.go
generated
vendored
64
vendor/code.gitea.io/sdk/gitea/user_email.go
generated
vendored
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Email an email address belonging to a user
|
||||
type Email struct {
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"verified"`
|
||||
Primary bool `json:"primary"`
|
||||
}
|
||||
|
||||
// ListEmailsOptions options for listing current's user emails
|
||||
type ListEmailsOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListEmails all the email addresses of user
|
||||
func (c *Client) ListEmails(opt ListEmailsOptions) ([]*Email, *Response, error) {
|
||||
opt.setDefaults()
|
||||
emails := make([]*Email, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/emails?%s", opt.getURLQuery().Encode()), nil, nil, &emails)
|
||||
return emails, resp, err
|
||||
}
|
||||
|
||||
// CreateEmailOption options when creating email addresses
|
||||
type CreateEmailOption struct {
|
||||
// email addresses to add
|
||||
Emails []string `json:"emails"`
|
||||
}
|
||||
|
||||
// AddEmail add one email to current user with options
|
||||
func (c *Client) AddEmail(opt CreateEmailOption) ([]*Email, *Response, error) {
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
emails := make([]*Email, 0, 3)
|
||||
resp, err := c.getParsedResponse("POST", "/user/emails", jsonHeader, bytes.NewReader(body), &emails)
|
||||
return emails, resp, err
|
||||
}
|
||||
|
||||
// DeleteEmailOption options when deleting email addresses
|
||||
type DeleteEmailOption struct {
|
||||
// email addresses to delete
|
||||
Emails []string `json:"emails"`
|
||||
}
|
||||
|
||||
// DeleteEmail delete one email of current users'
|
||||
func (c *Client) DeleteEmail(opt DeleteEmailOption) (*Response, error) {
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", "/user/emails", jsonHeader, bytes.NewReader(body))
|
||||
return resp, err
|
||||
}
|
93
vendor/code.gitea.io/sdk/gitea/user_follow.go
generated
vendored
93
vendor/code.gitea.io/sdk/gitea/user_follow.go
generated
vendored
|
@ -1,93 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ListFollowersOptions options for listing followers
|
||||
type ListFollowersOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListMyFollowers list all the followers of current user
|
||||
func (c *Client) ListMyFollowers(opt ListFollowersOptions) ([]*User, *Response, error) {
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/followers?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// ListFollowers list all the followers of one user
|
||||
func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// ListFollowingOptions options for listing a user's users being followed
|
||||
type ListFollowingOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListMyFollowing list all the users current user followed
|
||||
func (c *Client) ListMyFollowing(opt ListFollowingOptions) ([]*User, *Response, error) {
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/following?%s", opt.getURLQuery().Encode()), nil, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// ListFollowing list all the users the user followed
|
||||
func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
users := make([]*User, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
|
||||
return users, resp, err
|
||||
}
|
||||
|
||||
// IsFollowing if current user followed the target
|
||||
func (c *Client) IsFollowing(target string) (bool, *Response) {
|
||||
if err := escapeValidatePathSegments(&target); err != nil {
|
||||
// ToDo return err
|
||||
return false, nil
|
||||
}
|
||||
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||
return err == nil, resp
|
||||
}
|
||||
|
||||
// IsUserFollowing if the user followed the target
|
||||
func (c *Client) IsUserFollowing(user, target string) (bool, *Response) {
|
||||
if err := escapeValidatePathSegments(&user, &target); err != nil {
|
||||
// ToDo return err
|
||||
return false, nil
|
||||
}
|
||||
_, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil)
|
||||
return err == nil, resp
|
||||
}
|
||||
|
||||
// Follow set current user follow the target
|
||||
func (c *Client) Follow(target string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Unfollow set current user unfollow the target
|
||||
func (c *Client) Unfollow(target string) (*Response, error) {
|
||||
if err := escapeValidatePathSegments(&target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil)
|
||||
return resp, err
|
||||
}
|
89
vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
generated
vendored
89
vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
generated
vendored
|
@ -1,89 +0,0 @@
|
|||
// Copyright 2017 Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GPGKey a user GPG key to sign commit and tag in repository
|
||||
type GPGKey struct {
|
||||
ID int64 `json:"id"`
|
||||
PrimaryKeyID string `json:"primary_key_id"`
|
||||
KeyID string `json:"key_id"`
|
||||
PublicKey string `json:"public_key"`
|
||||
Emails []*GPGKeyEmail `json:"emails"`
|
||||
SubsKey []*GPGKey `json:"subkeys"`
|
||||
CanSign bool `json:"can_sign"`
|
||||
CanEncryptComms bool `json:"can_encrypt_comms"`
|
||||
CanEncryptStorage bool `json:"can_encrypt_storage"`
|
||||
CanCertify bool `json:"can_certify"`
|
||||
Created time.Time `json:"created_at,omitempty"`
|
||||
Expires time.Time `json:"expires_at,omitempty"`
|
||||
}
|
||||
|
||||
// GPGKeyEmail an email attached to a GPGKey
|
||||
type GPGKeyEmail struct {
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
// ListGPGKeysOptions options for listing a user's GPGKeys
|
||||
type ListGPGKeysOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListGPGKeys list all the GPG keys of the user
|
||||
func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
keys := make([]*GPGKey, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||
return keys, resp, err
|
||||
}
|
||||
|
||||
// ListMyGPGKeys list all the GPG keys of current user
|
||||
func (c *Client) ListMyGPGKeys(opt *ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
|
||||
opt.setDefaults()
|
||||
keys := make([]*GPGKey, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||
return keys, resp, err
|
||||
}
|
||||
|
||||
// GetGPGKey get current user's GPG key by key id
|
||||
func (c *Client) GetGPGKey(keyID int64) (*GPGKey, *Response, error) {
|
||||
key := new(GPGKey)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil, &key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// CreateGPGKeyOption options create user GPG key
|
||||
type CreateGPGKeyOption struct {
|
||||
// An armored GPG key to add
|
||||
//
|
||||
ArmoredKey string `json:"armored_public_key"`
|
||||
}
|
||||
|
||||
// CreateGPGKey create GPG key with options
|
||||
func (c *Client) CreateGPGKey(opt CreateGPGKeyOption) (*GPGKey, *Response, error) {
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key := new(GPGKey)
|
||||
resp, err := c.getParsedResponse("POST", "/user/gpg_keys", jsonHeader, bytes.NewReader(body), key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// DeleteGPGKey delete GPG key with key id
|
||||
func (c *Client) DeleteGPGKey(keyID int64) (*Response, error) {
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil)
|
||||
return resp, err
|
||||
}
|
83
vendor/code.gitea.io/sdk/gitea/user_key.go
generated
vendored
83
vendor/code.gitea.io/sdk/gitea/user_key.go
generated
vendored
|
@ -1,83 +0,0 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PublicKey publickey is a user key to push code to repository
|
||||
type PublicKey struct {
|
||||
ID int64 `json:"id"`
|
||||
Key string `json:"key"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Created time.Time `json:"created_at,omitempty"`
|
||||
Owner *User `json:"user,omitempty"`
|
||||
ReadOnly bool `json:"read_only,omitempty"`
|
||||
KeyType string `json:"key_type,omitempty"`
|
||||
}
|
||||
|
||||
// ListPublicKeysOptions options for listing a user's PublicKeys
|
||||
type ListPublicKeysOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// ListPublicKeys list all the public keys of the user
|
||||
func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
|
||||
if err := escapeValidatePathSegments(&user); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
opt.setDefaults()
|
||||
keys := make([]*PublicKey, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||
return keys, resp, err
|
||||
}
|
||||
|
||||
// ListMyPublicKeys list all the public keys of current user
|
||||
func (c *Client) ListMyPublicKeys(opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
|
||||
opt.setDefaults()
|
||||
keys := make([]*PublicKey, 0, opt.PageSize)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
|
||||
return keys, resp, err
|
||||
}
|
||||
|
||||
// GetPublicKey get current user's public key by key id
|
||||
func (c *Client) GetPublicKey(keyID int64) (*PublicKey, *Response, error) {
|
||||
key := new(PublicKey)
|
||||
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/keys/%d", keyID), nil, nil, &key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// CreateKeyOption options when creating a key
|
||||
type CreateKeyOption struct {
|
||||
// Title of the key to add
|
||||
Title string `json:"title"`
|
||||
// An armored SSH key to add
|
||||
Key string `json:"key"`
|
||||
// Describe if the key has only read access or read/write
|
||||
ReadOnly bool `json:"read_only"`
|
||||
}
|
||||
|
||||
// CreatePublicKey create public key with options
|
||||
func (c *Client) CreatePublicKey(opt CreateKeyOption) (*PublicKey, *Response, error) {
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
key := new(PublicKey)
|
||||
resp, err := c.getParsedResponse("POST", "/user/keys", jsonHeader, bytes.NewReader(body), key)
|
||||
return key, resp, err
|
||||
}
|
||||
|
||||
// DeletePublicKey delete public key with key id
|
||||
func (c *Client) DeletePublicKey(keyID int64) (*Response, error) {
|
||||
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/keys/%d", keyID), nil, nil)
|
||||
return resp, err
|
||||
}
|
48
vendor/code.gitea.io/sdk/gitea/user_search.go
generated
vendored
48
vendor/code.gitea.io/sdk/gitea/user_search.go
generated
vendored
|
@ -1,48 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type searchUsersResponse struct {
|
||||
Users []*User `json:"data"`
|
||||
}
|
||||
|
||||
// SearchUsersOption options for SearchUsers
|
||||
type SearchUsersOption struct {
|
||||
ListOptions
|
||||
KeyWord string
|
||||
}
|
||||
|
||||
// QueryEncode turns options into querystring argument
|
||||
func (opt *SearchUsersOption) QueryEncode() string {
|
||||
query := make(url.Values)
|
||||
if opt.Page > 0 {
|
||||
query.Add("page", fmt.Sprintf("%d", opt.Page))
|
||||
}
|
||||
if opt.PageSize > 0 {
|
||||
query.Add("limit", fmt.Sprintf("%d", opt.PageSize))
|
||||
}
|
||||
if len(opt.KeyWord) > 0 {
|
||||
query.Add("q", opt.KeyWord)
|
||||
}
|
||||
return query.Encode()
|
||||
}
|
||||
|
||||
func (c *Client) searchUsers(rawQuery string) ([]*User, *Response, error) {
|
||||
link, _ := url.Parse("/users/search")
|
||||
link.RawQuery = rawQuery
|
||||
userResp := new(searchUsersResponse)
|
||||
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &userResp)
|
||||
return userResp.Users, resp, err
|
||||
}
|
||||
|
||||
// SearchUsers finds users by query
|
||||
func (c *Client) SearchUsers(opt SearchUsersOption) ([]*User, *Response, error) {
|
||||
return c.searchUsers(opt.QueryEncode())
|
||||
}
|
62
vendor/code.gitea.io/sdk/gitea/user_settings.go
generated
vendored
62
vendor/code.gitea.io/sdk/gitea/user_settings.go
generated
vendored
|
@ -1,62 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// UserSettings represents user settings
|
||||
type UserSettings struct {
|
||||
FullName string `json:"full_name"`
|
||||
Website string `json:"website"`
|
||||
Description string `json:"description"`
|
||||
Location string `json:"location"`
|
||||
Language string `json:"language"`
|
||||
Theme string `json:"theme"`
|
||||
DiffViewStyle string `json:"diff_view_style"`
|
||||
// Privacy
|
||||
HideEmail bool `json:"hide_email"`
|
||||
HideActivity bool `json:"hide_activity"`
|
||||
}
|
||||
|
||||
// UserSettingsOptions represents options to change user settings
|
||||
type UserSettingsOptions struct {
|
||||
FullName *string `json:"full_name,omitempty"`
|
||||
Website *string `json:"website,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Location *string `json:"location,omitempty"`
|
||||
Language *string `json:"language,omitempty"`
|
||||
Theme *string `json:"theme,omitempty"`
|
||||
DiffViewStyle *string `json:"diff_view_style,omitempty"`
|
||||
// Privacy
|
||||
HideEmail *bool `json:"hide_email,omitempty"`
|
||||
HideActivity *bool `json:"hide_activity,omitempty"`
|
||||
}
|
||||
|
||||
// GetUserSettings returns user settings
|
||||
func (c *Client) GetUserSettings() (*UserSettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
userConfig := new(UserSettings)
|
||||
resp, err := c.getParsedResponse("GET", "/user/settings", nil, nil, userConfig)
|
||||
return userConfig, resp, err
|
||||
}
|
||||
|
||||
// UpdateUserSettings returns user settings
|
||||
func (c *Client) UpdateUserSettings(opt UserSettingsOptions) (*UserSettings, *Response, error) {
|
||||
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
body, err := json.Marshal(&opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
userConfig := new(UserSettings)
|
||||
resp, err := c.getParsedResponse("PATCH", "/user/settings", jsonHeader, bytes.NewReader(body), userConfig)
|
||||
return userConfig, resp, err
|
||||
}
|
79
vendor/code.gitea.io/sdk/gitea/version.go
generated
vendored
79
vendor/code.gitea.io/sdk/gitea/version.go
generated
vendored
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
// ServerVersion returns the version of the server
|
||||
func (c *Client) ServerVersion() (string, *Response, error) {
|
||||
var v = struct {
|
||||
Version string `json:"version"`
|
||||
}{}
|
||||
resp, err := c.getParsedResponse("GET", "/version", nil, nil, &v)
|
||||
return v.Version, resp, err
|
||||
}
|
||||
|
||||
// CheckServerVersionConstraint validates that the login's server satisfies a
|
||||
// given version constraint such as ">= 1.11.0+dev"
|
||||
func (c *Client) CheckServerVersionConstraint(constraint string) error {
|
||||
if err := c.loadServerVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
check, err := version.NewConstraint(constraint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !check.Check(c.serverVersion) {
|
||||
c.mutex.RLock()
|
||||
url := c.url
|
||||
c.mutex.RUnlock()
|
||||
return fmt.Errorf("gitea server at %s does not satisfy version constraint %s", url, constraint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// predefined versions only have to be parsed by library once
|
||||
var (
|
||||
version1_11_0, _ = version.NewVersion("1.11.0")
|
||||
version1_12_0, _ = version.NewVersion("1.12.0")
|
||||
version1_13_0, _ = version.NewVersion("1.13.0")
|
||||
version1_14_0, _ = version.NewVersion("1.14.0")
|
||||
version1_15_0, _ = version.NewVersion("1.15.0")
|
||||
)
|
||||
|
||||
// checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease
|
||||
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
|
||||
if err := c.loadServerVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.serverVersion.GreaterThanOrEqual(v) {
|
||||
c.mutex.RLock()
|
||||
url := c.url
|
||||
c.mutex.RUnlock()
|
||||
return fmt.Errorf("gitea server at %s is older than %s", url, v.Original())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadServerVersion init the serverVersion variable
|
||||
func (c *Client) loadServerVersion() (err error) {
|
||||
c.getVersionOnce.Do(func() {
|
||||
raw, _, err2 := c.ServerVersion()
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
if c.serverVersion, err = version.NewVersion(raw); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
459
vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go
generated
vendored
459
vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go
generated
vendored
|
@ -1,459 +0,0 @@
|
|||
package unidiff
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.com/noerw/unidiff-comments/types"
|
||||
)
|
||||
|
||||
const (
|
||||
stateStartOfDiff = "stateStartOfDiff"
|
||||
stateDiffHeader = "stateDiffHeader"
|
||||
stateHunkHeader = "stateHunkHeader"
|
||||
stateHunkBody = "stateHunkBody"
|
||||
stateComment = "stateComment"
|
||||
stateCommentDelim = "stateCommentDelim"
|
||||
stateCommentHeader = "stateCommentHeader"
|
||||
stateDiffComment = "stateDiffComment"
|
||||
stateDiffCommentDelim = "stateDiffCommentDelim"
|
||||
stateDiffCommentHeader = "stateDiffCommentHeader"
|
||||
|
||||
ignorePrefix = "###"
|
||||
)
|
||||
|
||||
var (
|
||||
reDiffHeader = regexp.MustCompile(
|
||||
`^--- |^\+\+\+ `)
|
||||
|
||||
reGitDiffHeader = regexp.MustCompile(
|
||||
`^diff |^index `)
|
||||
|
||||
reFromFile = regexp.MustCompile(
|
||||
`^--- (\S+)(\s+(.*))`)
|
||||
|
||||
reToFile = regexp.MustCompile(
|
||||
`^\+\+\+ (\S+)(\s+(.*))`)
|
||||
|
||||
reHunk = regexp.MustCompile(
|
||||
`^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@`)
|
||||
|
||||
reSegmentContext = regexp.MustCompile(
|
||||
`^ `)
|
||||
|
||||
reSegmentAdded = regexp.MustCompile(
|
||||
`^\+`)
|
||||
|
||||
reSegmentRemoved = regexp.MustCompile(
|
||||
`^-`)
|
||||
|
||||
reCommentDelim = regexp.MustCompile(
|
||||
`^#\s+---`)
|
||||
|
||||
reCommentHeader = regexp.MustCompile(
|
||||
`^#\s+\[(\d+)@(\d+)\]\s+\|([^|]+)\|(.*)`)
|
||||
|
||||
reCommentText = regexp.MustCompile(
|
||||
`^#(\s*)(.*)\s*`)
|
||||
|
||||
reIndent = regexp.MustCompile(
|
||||
`^#(\s+)`)
|
||||
|
||||
reEmptyLine = regexp.MustCompile(
|
||||
`^\n$`)
|
||||
|
||||
reIgnoredLine = regexp.MustCompile(
|
||||
`^` + ignorePrefix)
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
state string
|
||||
changeset types.Changeset
|
||||
diff *types.Diff
|
||||
hunk *types.Hunk
|
||||
segment *types.Segment
|
||||
comment *types.Comment
|
||||
line *types.Line
|
||||
lineNumber int
|
||||
|
||||
segmentType string
|
||||
commentsList []*types.Comment
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
LineNumber int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err Error) Error() string {
|
||||
return fmt.Sprintf("line %d: %s", err.LineNumber, err.Message)
|
||||
}
|
||||
|
||||
func ReadChangeset(r io.Reader) (types.Changeset, error) {
|
||||
buffer := bufio.NewReader(r)
|
||||
|
||||
current := parser{}
|
||||
current.state = stateStartOfDiff
|
||||
|
||||
for {
|
||||
current.lineNumber++
|
||||
|
||||
line, err := buffer.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if reIgnoredLine.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = current.switchState(line)
|
||||
if err != nil {
|
||||
return current.changeset, err
|
||||
}
|
||||
|
||||
err = current.createNodes(line)
|
||||
if err != nil {
|
||||
return current.changeset, err
|
||||
}
|
||||
|
||||
err = current.locateNodes(line)
|
||||
if err != nil {
|
||||
return current.changeset, err
|
||||
}
|
||||
|
||||
err = current.parseLine(line)
|
||||
if err != nil {
|
||||
return current.changeset, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, comment := range current.commentsList {
|
||||
comment.Text = strings.TrimSpace(comment.Text)
|
||||
}
|
||||
|
||||
return current.changeset, nil
|
||||
}
|
||||
|
||||
func (current *parser) switchState(line string) error {
|
||||
inComment := false
|
||||
|
||||
switch current.state {
|
||||
case stateStartOfDiff:
|
||||
switch {
|
||||
case reDiffHeader.MatchString(line), reGitDiffHeader.MatchString(line):
|
||||
current.state = stateDiffHeader
|
||||
case reCommentText.MatchString(line):
|
||||
inComment = true
|
||||
case reEmptyLine.MatchString(line):
|
||||
// body intentionally left empty
|
||||
default:
|
||||
return Error{
|
||||
current.lineNumber,
|
||||
"expected diff header, but none found",
|
||||
}
|
||||
}
|
||||
case stateDiffHeader:
|
||||
switch {
|
||||
case reHunk.MatchString(line):
|
||||
current.state = stateHunkHeader
|
||||
}
|
||||
case stateDiffComment, stateDiffCommentDelim, stateDiffCommentHeader:
|
||||
switch {
|
||||
case reDiffHeader.MatchString(line), reGitDiffHeader.MatchString(line):
|
||||
current.state = stateDiffHeader
|
||||
case reCommentText.MatchString(line):
|
||||
inComment = true
|
||||
case reEmptyLine.MatchString(line):
|
||||
current.state = stateStartOfDiff
|
||||
}
|
||||
case stateHunkHeader:
|
||||
current.state = stateHunkBody
|
||||
fallthrough
|
||||
case stateHunkBody, stateComment, stateCommentDelim, stateCommentHeader:
|
||||
switch {
|
||||
case reSegmentContext.MatchString(line):
|
||||
current.state = stateHunkBody
|
||||
current.segmentType = types.SegmentTypeContext
|
||||
case reSegmentRemoved.MatchString(line):
|
||||
current.state = stateHunkBody
|
||||
current.segmentType = types.SegmentTypeRemoved
|
||||
case reSegmentAdded.MatchString(line):
|
||||
current.state = stateHunkBody
|
||||
current.segmentType = types.SegmentTypeAdded
|
||||
case reHunk.MatchString(line):
|
||||
current.state = stateHunkHeader
|
||||
case reCommentText.MatchString(line):
|
||||
inComment = true
|
||||
case reGitDiffHeader.MatchString(line):
|
||||
current.state = stateDiffHeader
|
||||
current.diff = nil
|
||||
current.hunk = nil
|
||||
current.segment = nil
|
||||
current.line = nil
|
||||
case reEmptyLine.MatchString(line):
|
||||
current.state = stateStartOfDiff
|
||||
current.diff = nil
|
||||
current.hunk = nil
|
||||
current.segment = nil
|
||||
current.line = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !inComment {
|
||||
current.comment = nil
|
||||
} else {
|
||||
switch current.state {
|
||||
case stateStartOfDiff:
|
||||
fallthrough
|
||||
case stateDiffComment, stateDiffCommentDelim, stateDiffCommentHeader:
|
||||
switch {
|
||||
case reCommentDelim.MatchString(line):
|
||||
current.state = stateDiffCommentDelim
|
||||
case reCommentHeader.MatchString(line):
|
||||
current.state = stateDiffCommentHeader
|
||||
case reCommentText.MatchString(line):
|
||||
current.state = stateDiffComment
|
||||
}
|
||||
case stateHunkBody:
|
||||
fallthrough
|
||||
case stateComment, stateCommentDelim, stateCommentHeader:
|
||||
switch {
|
||||
case reCommentDelim.MatchString(line):
|
||||
current.state = stateCommentDelim
|
||||
case reCommentHeader.MatchString(line):
|
||||
current.state = stateCommentHeader
|
||||
case reCommentText.MatchString(line):
|
||||
current.state = stateComment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment for debug state switching
|
||||
// fmt.Printf("%20s : %#v\n", current.state, line)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) createNodes(line string) error {
|
||||
switch current.state {
|
||||
case stateDiffComment:
|
||||
if current.comment != nil {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case stateDiffCommentDelim, stateDiffCommentHeader:
|
||||
current.comment = &types.Comment{}
|
||||
fallthrough
|
||||
case stateDiffHeader:
|
||||
if current.diff == nil {
|
||||
current.diff = &types.Diff{}
|
||||
current.changeset.Diffs = append(current.changeset.Diffs,
|
||||
current.diff)
|
||||
}
|
||||
case stateHunkHeader:
|
||||
current.hunk = &types.Hunk{}
|
||||
current.segment = &types.Segment{}
|
||||
case stateCommentDelim, stateCommentHeader:
|
||||
current.comment = &types.Comment{}
|
||||
case stateComment:
|
||||
if current.comment == nil {
|
||||
current.comment = &types.Comment{}
|
||||
}
|
||||
case stateHunkBody:
|
||||
if current.segment.Type != current.segmentType {
|
||||
current.segment = &types.Segment{Type: current.segmentType}
|
||||
current.hunk.Segments = append(current.hunk.Segments,
|
||||
current.segment)
|
||||
}
|
||||
|
||||
current.line = &types.Line{}
|
||||
current.segment.Lines = append(current.segment.Lines, current.line)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) locateNodes(line string) error {
|
||||
switch current.state {
|
||||
case stateComment, stateDiffComment:
|
||||
current.locateComment(line)
|
||||
case stateHunkBody:
|
||||
current.locateLine(line)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) locateComment(line string) error {
|
||||
if current.comment.Parented || strings.TrimSpace(line) == "#" {
|
||||
return nil
|
||||
}
|
||||
|
||||
current.commentsList = append(current.commentsList, current.comment)
|
||||
current.comment.Parented = true
|
||||
|
||||
if current.hunk != nil {
|
||||
current.comment.Anchor.LineType = current.segment.Type
|
||||
current.comment.Anchor.Line = current.segment.GetLineNum(current.line)
|
||||
current.comment.Anchor.Path = current.diff.Destination.ToString
|
||||
current.comment.Anchor.SrcPath = current.diff.Source.ToString
|
||||
}
|
||||
|
||||
current.comment.Indent = getIndentSize(line)
|
||||
|
||||
parent := current.findParentComment(current.comment)
|
||||
if parent != nil {
|
||||
parent.Comments = append(parent.Comments, current.comment)
|
||||
} else {
|
||||
if current.line != nil {
|
||||
current.diff.LineComments = append(current.diff.LineComments,
|
||||
current.comment)
|
||||
current.line.Comments = append(current.line.Comments,
|
||||
current.comment)
|
||||
} else {
|
||||
current.diff.FileComments = append(current.diff.FileComments,
|
||||
current.comment)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) locateLine(line string) error {
|
||||
sourceOffset := current.hunk.SourceLine - 1
|
||||
destinationOffset := current.hunk.DestinationLine - 1
|
||||
if len(current.hunk.Segments) > 1 {
|
||||
prevSegment := current.hunk.Segments[len(current.hunk.Segments)-2]
|
||||
lastLine := prevSegment.Lines[len(prevSegment.Lines)-1]
|
||||
sourceOffset = lastLine.Source
|
||||
destinationOffset = lastLine.Destination
|
||||
}
|
||||
hunkLength := int64(len(current.segment.Lines))
|
||||
switch current.segment.Type {
|
||||
case types.SegmentTypeContext:
|
||||
current.line.Source = sourceOffset + hunkLength
|
||||
current.line.Destination = destinationOffset + hunkLength
|
||||
case types.SegmentTypeAdded:
|
||||
current.line.Source = sourceOffset
|
||||
current.line.Destination = destinationOffset + hunkLength
|
||||
case types.SegmentTypeRemoved:
|
||||
current.line.Source = sourceOffset + hunkLength
|
||||
current.line.Destination = destinationOffset
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseLine(line string) error {
|
||||
switch current.state {
|
||||
case stateDiffHeader:
|
||||
current.parseDiffHeader(line)
|
||||
case stateHunkHeader:
|
||||
current.parseHunkHeader(line)
|
||||
case stateHunkBody:
|
||||
current.parseHunkBody(line)
|
||||
case stateComment, stateDiffComment:
|
||||
current.parseComment(line)
|
||||
case stateCommentHeader, stateDiffCommentHeader:
|
||||
current.parseCommentHeader(line)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseDiffHeader(line string) error {
|
||||
switch {
|
||||
case reFromFile.MatchString(line):
|
||||
matches := reFromFile.FindStringSubmatch(line)
|
||||
current.changeset.Path = matches[1]
|
||||
current.diff.Source.ToString = matches[1]
|
||||
current.changeset.FromHash = matches[3]
|
||||
current.diff.Attributes.FromHash = []string{matches[3]}
|
||||
case reToFile.MatchString(line):
|
||||
matches := reToFile.FindStringSubmatch(line)
|
||||
current.diff.Destination.ToString = matches[1]
|
||||
current.changeset.ToHash = matches[3]
|
||||
current.diff.Attributes.ToHash = []string{matches[3]}
|
||||
default:
|
||||
return Error{
|
||||
current.lineNumber,
|
||||
"expected diff header, but not found",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseHunkHeader(line string) error {
|
||||
matches := reHunk.FindStringSubmatch(line)
|
||||
current.hunk.SourceLine, _ = strconv.ParseInt(matches[1], 10, 64)
|
||||
current.hunk.SourceSpan, _ = strconv.ParseInt(matches[3], 10, 64)
|
||||
current.hunk.DestinationLine, _ = strconv.ParseInt(matches[4], 10, 64)
|
||||
current.hunk.DestinationSpan, _ = strconv.ParseInt(matches[6], 10, 64)
|
||||
current.diff.Hunks = append(current.diff.Hunks, current.hunk)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseHunkBody(line string) error {
|
||||
current.line.Line = line[1 : len(line)-1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseCommentHeader(line string) error {
|
||||
matches := reCommentHeader.FindStringSubmatch(line)
|
||||
current.comment.Author.DisplayName = strings.TrimSpace(matches[3])
|
||||
current.comment.Id, _ = strconv.ParseInt(matches[1], 10, 64)
|
||||
updatedDate, _ := time.ParseInLocation(time.ANSIC,
|
||||
strings.TrimSpace(matches[4]),
|
||||
time.Local)
|
||||
current.comment.UpdatedDate = types.UnixTimestamp(updatedDate.Unix() * 1000)
|
||||
|
||||
version, _ := strconv.ParseInt(matches[2], 10, 64)
|
||||
current.comment.Version = int(version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) parseComment(line string) error {
|
||||
matches := reCommentText.FindStringSubmatch(line)
|
||||
if len(matches[1]) < current.comment.Indent {
|
||||
return Error{
|
||||
LineNumber: current.lineNumber,
|
||||
Message: fmt.Sprintf(
|
||||
"unexpected indent, should be at least: %d",
|
||||
current.comment.Indent,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
indentedLine := matches[1][current.comment.Indent:] + matches[2]
|
||||
current.comment.Text += "\n" + indentedLine
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *parser) findParentComment(comment *types.Comment) *types.Comment {
|
||||
for i := len(current.commentsList) - 1; i >= 0; i-- {
|
||||
c := current.commentsList[i]
|
||||
if comment.Indent > c.Indent {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getIndentSize(line string) int {
|
||||
matches := reIndent.FindStringSubmatch(line)
|
||||
if len(matches) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(matches[1])
|
||||
}
|
5
vendor/gitea.com/noerw/unidiff-comments/go.mod
generated
vendored
5
vendor/gitea.com/noerw/unidiff-comments/go.mod
generated
vendored
|
@ -1,5 +0,0 @@
|
|||
module gitea.com/noerw/unidiff-comments
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597
|
2
vendor/gitea.com/noerw/unidiff-comments/go.sum
generated
vendored
2
vendor/gitea.com/noerw/unidiff-comments/go.sum
generated
vendored
|
@ -1,2 +0,0 @@
|
|||
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA=
|
||||
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw=
|
47
vendor/gitea.com/noerw/unidiff-comments/types/changeset.go
generated
vendored
47
vendor/gitea.com/noerw/unidiff-comments/types/changeset.go
generated
vendored
|
@ -1,47 +0,0 @@
|
|||
package types
|
||||
|
||||
type Changeset struct {
|
||||
FromHash string
|
||||
ToHash string
|
||||
Path string
|
||||
Whitespace string
|
||||
Diffs []*Diff
|
||||
}
|
||||
|
||||
func (r Changeset) ForEachComment(callback func(*Diff, *Comment, *Comment)) {
|
||||
for _, diff := range r.Diffs {
|
||||
stack := make([]*Comment, 0)
|
||||
parents := make(map[*Comment]*Comment)
|
||||
stack = append(stack, diff.FileComments...)
|
||||
stack = append(stack, diff.LineComments...)
|
||||
pos := 0
|
||||
|
||||
for pos < len(stack) {
|
||||
comment := stack[pos]
|
||||
|
||||
if comment.Comments != nil {
|
||||
stack = append(stack, comment.Comments...)
|
||||
for _, c := range comment.Comments {
|
||||
parents[c] = comment
|
||||
}
|
||||
}
|
||||
|
||||
callback(diff, comment, parents[comment])
|
||||
|
||||
pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r Changeset) ForEachLine(
|
||||
callback func(*Diff, *Hunk, *Segment, *Line) error,
|
||||
) error {
|
||||
for _, diff := range r.Diffs {
|
||||
err := diff.ForEachLine(callback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
121
vendor/gitea.com/noerw/unidiff-comments/types/comment.go
generated
vendored
121
vendor/gitea.com/noerw/unidiff-comments/types/comment.go
generated
vendored
|
@ -1,121 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UnixTimestamp int
|
||||
|
||||
func (u UnixTimestamp) String() string {
|
||||
return time.Unix(int64(u/1000), 0).Format(time.ANSIC)
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int64
|
||||
Version int
|
||||
Text string
|
||||
CreatedDate UnixTimestamp
|
||||
UpdatedDate UnixTimestamp
|
||||
Comments CommentsTree
|
||||
Author struct {
|
||||
Name string
|
||||
EmailAddress string
|
||||
Id int
|
||||
DisplayName string
|
||||
Active bool
|
||||
Slug string
|
||||
Type string
|
||||
}
|
||||
|
||||
Anchor CommentAnchor
|
||||
|
||||
PermittedOperations struct {
|
||||
Editable bool
|
||||
Deletable bool
|
||||
}
|
||||
|
||||
Indent int
|
||||
Parented bool
|
||||
}
|
||||
|
||||
type CommentAnchor struct {
|
||||
FromHash string
|
||||
ToHash string
|
||||
Line int64 `json:"line"`
|
||||
LineType string `json:"lineType"`
|
||||
Path string `json:"path"`
|
||||
SrcPath string `json:"srcPath"`
|
||||
FileType string `json:"fileType"`
|
||||
}
|
||||
|
||||
type CommentsTree []*Comment
|
||||
|
||||
//const replyIndent = " "
|
||||
|
||||
var begOfLineRe = regexp.MustCompile("(?m)^")
|
||||
|
||||
//func (c Comment) String() string {
|
||||
// comments, _ := commentTpl.Execute(c)
|
||||
|
||||
// for _, reply := range c.Comments {
|
||||
// comments += reply.AsReply()
|
||||
// }
|
||||
|
||||
// return comments
|
||||
//}
|
||||
|
||||
//func (c Comment) AsReply() string {
|
||||
// return begOfLineRe.ReplaceAllString(
|
||||
// commentSpacing+c.String(),
|
||||
// replyIndent,
|
||||
// )
|
||||
//}
|
||||
|
||||
var reWhiteSpace = regexp.MustCompile(`\s+`)
|
||||
|
||||
func (c Comment) Short(length int) string {
|
||||
sticked := []rune(reWhiteSpace.ReplaceAllString(c.Text, " "))
|
||||
|
||||
if len(sticked) > length {
|
||||
return string(sticked[:length]) + "..."
|
||||
} else {
|
||||
return string(sticked)
|
||||
}
|
||||
}
|
||||
|
||||
const ignorePrefix = "###"
|
||||
|
||||
var reBeginningOfLine = regexp.MustCompile(`(?m)^`)
|
||||
var reIgnorePrefixSpace = regexp.MustCompile("(?m)^" + ignorePrefix + " $")
|
||||
|
||||
func Note(String string) string {
|
||||
return reIgnorePrefixSpace.ReplaceAllString(
|
||||
reBeginningOfLine.ReplaceAllString(String, ignorePrefix+" "),
|
||||
ignorePrefix)
|
||||
}
|
||||
|
||||
//const commentSpacing = "\n\n"
|
||||
//const commentPrefix = "# "
|
||||
|
||||
//func (comments CommentsTree) String() string {
|
||||
// res := ""
|
||||
|
||||
// if len(comments) > 0 {
|
||||
// res = "---" + commentSpacing
|
||||
// }
|
||||
|
||||
// for i, comment := range comments {
|
||||
// res += comment.String()
|
||||
// if i < len(comments)-1 {
|
||||
// res += commentSpacing
|
||||
// }
|
||||
// }
|
||||
|
||||
// if len(comments) > 0 {
|
||||
// return danglingSpacesRe.ReplaceAllString(
|
||||
// begOfLineRe.ReplaceAllString(res, "# "), "")
|
||||
// } else {
|
||||
// return ""
|
||||
// }
|
||||
//}
|
61
vendor/gitea.com/noerw/unidiff-comments/types/diff.go
generated
vendored
61
vendor/gitea.com/noerw/unidiff-comments/types/diff.go
generated
vendored
|
@ -1,61 +0,0 @@
|
|||
package types
|
||||
|
||||
type Diff struct {
|
||||
Truncated bool
|
||||
Source struct {
|
||||
Parent string
|
||||
Name string
|
||||
ToString string
|
||||
}
|
||||
Destination struct {
|
||||
Parent string
|
||||
Name string
|
||||
ToString string
|
||||
}
|
||||
Hunks []*Hunk
|
||||
|
||||
FileComments CommentsTree
|
||||
LineComments CommentsTree
|
||||
|
||||
Note string
|
||||
|
||||
// Lists made only for Stash API compatibility.
|
||||
// TODO: move it to `ash`.
|
||||
Attributes struct {
|
||||
FromHash []string
|
||||
ToHash []string
|
||||
}
|
||||
}
|
||||
|
||||
func (d Diff) GetHashFrom() string {
|
||||
if len(d.Attributes.FromHash) > 0 {
|
||||
return d.Attributes.FromHash[0]
|
||||
} else {
|
||||
return "???"
|
||||
}
|
||||
}
|
||||
|
||||
func (d Diff) GetHashTo() string {
|
||||
if len(d.Attributes.ToHash) > 0 {
|
||||
return d.Attributes.ToHash[0]
|
||||
} else {
|
||||
return "???"
|
||||
}
|
||||
}
|
||||
|
||||
func (d Diff) ForEachLine(
|
||||
callback func(*Diff, *Hunk, *Segment, *Line) error,
|
||||
) error {
|
||||
for _, hunk := range d.Hunks {
|
||||
for _, segment := range hunk.Segments {
|
||||
for _, line := range segment.Lines {
|
||||
err := callback(&d, hunk, segment, line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
10
vendor/gitea.com/noerw/unidiff-comments/types/hunk.go
generated
vendored
10
vendor/gitea.com/noerw/unidiff-comments/types/hunk.go
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
package types
|
||||
|
||||
type Hunk struct {
|
||||
SourceLine int64
|
||||
SourceSpan int64
|
||||
DestinationLine int64
|
||||
DestinationSpan int64
|
||||
Truncated bool
|
||||
Segments []*Segment
|
||||
}
|
29
vendor/gitea.com/noerw/unidiff-comments/types/line.go
generated
vendored
29
vendor/gitea.com/noerw/unidiff-comments/types/line.go
generated
vendored
|
@ -1,29 +0,0 @@
|
|||
package types
|
||||
|
||||
import "regexp"
|
||||
|
||||
type Line struct {
|
||||
Destination int64
|
||||
Source int64
|
||||
Line string
|
||||
Truncated bool
|
||||
ConflictMarker string
|
||||
CommentIds []int64
|
||||
Comments CommentsTree
|
||||
}
|
||||
|
||||
var danglingSpacesRe = regexp.MustCompile("(?m) +$")
|
||||
|
||||
//var lineTpl = tplutil.SparseTemplate("line", `
|
||||
//{{.Line}}
|
||||
|
||||
//{{if .Comments}}
|
||||
// {{"\n"}}
|
||||
// {{.Comments}}
|
||||
//{{end}}
|
||||
//`)
|
||||
|
||||
//func (l Line) String() string {
|
||||
// result, _ := lineTpl.Execute(l)
|
||||
// return result
|
||||
//}
|
39
vendor/gitea.com/noerw/unidiff-comments/types/segment.go
generated
vendored
39
vendor/gitea.com/noerw/unidiff-comments/types/segment.go
generated
vendored
|
@ -1,39 +0,0 @@
|
|||
package types
|
||||
|
||||
const (
|
||||
SegmentTypeContext = "CONTEXT"
|
||||
SegmentTypeRemoved = "REMOVED"
|
||||
SegmentTypeAdded = "ADDED"
|
||||
)
|
||||
|
||||
type Segment struct {
|
||||
Type string
|
||||
Truncated bool
|
||||
Lines []*Line
|
||||
}
|
||||
|
||||
func (s Segment) TextPrefix() string {
|
||||
switch s.Type {
|
||||
case SegmentTypeAdded:
|
||||
return "+"
|
||||
case SegmentTypeRemoved:
|
||||
return "-"
|
||||
case SegmentTypeContext:
|
||||
return " "
|
||||
default:
|
||||
return "?"
|
||||
}
|
||||
}
|
||||
|
||||
func (s Segment) GetLineNum(l *Line) int64 {
|
||||
switch s.Type {
|
||||
case SegmentTypeContext:
|
||||
fallthrough
|
||||
case SegmentTypeRemoved:
|
||||
return l.Source
|
||||
case SegmentTypeAdded:
|
||||
return l.Destination
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
22
vendor/github.com/AlecAivazis/survey/v2/.travis.yml
generated
vendored
22
vendor/github.com/AlecAivazis/survey/v2/.travis.yml
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.12
|
||||
|
||||
os:
|
||||
- linux
|
||||
- linux-ppc64le
|
||||
- osx
|
||||
- windows
|
||||
|
||||
go_import_path: github.com/AlecAivazis/survey/v2
|
||||
|
||||
before_install:
|
||||
- go get github.com/alecaivazis/run
|
||||
|
||||
install:
|
||||
- run install-deps
|
||||
|
||||
script:
|
||||
- run tests
|
||||
# - run autoplay-tests
|
77
vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md
generated
vendored
77
vendor/github.com/AlecAivazis/survey/v2/CONTRIBUTING.md
generated
vendored
|
@ -1,77 +0,0 @@
|
|||
# Contributing to Survey
|
||||
|
||||
🎉🎉 First off, thanks for the interest in contributing to `survey`! 🎉🎉
|
||||
|
||||
The following is a set of guidelines to follow when contributing to this package. These are not hard rules, please use common sense and feel free to propose changes to this document in a pull request.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Code of Conduct](#code-of-conduct)
|
||||
1. [Getting Help](#getting-help)
|
||||
1. [Filing a Bug Report](#how-to-file-a-bug-report)
|
||||
1. [Suggesting an API change](#suggesting-an-api-change)
|
||||
1. [Submitting a Contribution](#submitting-a-contribution)
|
||||
1. [Writing and Running Tests](#writing-and-running-tests)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and its contibutors are expected to uphold the [Go Community Code of Conduct](https://golang.org/conduct). By participating, you are expected to follow these guidelines.
|
||||
|
||||
## Getting help
|
||||
|
||||
Feel free to [open up an issue](https://github.com/AlecAivazis/survey/v2/issues/new) on GitHub when asking a question so others will be able to find it. Please remember to tag the issue with the `Question` label so the maintainers can get to your question as soon as possible. If the question is urgent, feel free to reach out to `@AlecAivazis` directly in the gophers slack channel.
|
||||
|
||||
## How to file a bug report
|
||||
|
||||
Bugs are tracked using the Github Issue tracker. When filing a bug, please remember to label the issue as a `Bug` and answer/provide the following:
|
||||
|
||||
1. What operating system and terminal are you using?
|
||||
1. An example that showcases the bug.
|
||||
1. What did you expect to see?
|
||||
1. What did you see instead?
|
||||
|
||||
## Suggesting an API change
|
||||
|
||||
If you have an idea, I'm more than happy to discuss it. Please open an issue and we can work through it. In order to maintain some sense of stability, additions to the top-level API are taken just as seriously as changes that break it. Adding stuff is much easier than removing it.
|
||||
|
||||
## Submitting a contribution
|
||||
|
||||
In order to maintain stability, most features get fully integrated in more than one PR. This allows for more opportunity to think through each API change without amassing large amounts of tech debt and API changes at once. If your feature can be broken into separate chunks, it will be able to be reviewed much quicker. For example, if the PR that implemented the `Validate` field was submitted in a PR separately from one that included `survey.Required`, it would be able to get merge without having to decide how many different `Validators` we want to provide as part of `survey`'s API.
|
||||
|
||||
When submitting a contribution,
|
||||
|
||||
- Provide a description of the feature or change
|
||||
- Reference the ticket addressed by the PR if there is one
|
||||
- Following community standards, add comments for all exported members so that all necessary information is available on godocs
|
||||
- Remember to update the project README.md with changes to the high-level API
|
||||
- Include both positive and negative unit tests (when applicable)
|
||||
- Contributions with visual ramifications or interaction changes should be accompanied with the appropriate `go-expect` tests. For more information on writing these tests, see [Writing and Running Tests](#writing-and-running-tests)
|
||||
|
||||
## Writing and running tests
|
||||
|
||||
When submitting features, please add as many units tests as necessary to test both positive and negative cases.
|
||||
|
||||
Integration tests for survey uses [go-expect](https://github.com/Netflix/go-expect) to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY, you need a way to interpret terminal / ANSI escape sequences for things like `CursorLocation`. The stdin/stdout handled by `go-expect` is also multiplexed to a [virtual terminal](https://github.com/hinshun/vt10x).
|
||||
|
||||
For example, you can extend the tests for Input by specifying the following test case:
|
||||
|
||||
```go
|
||||
{
|
||||
"Test Input prompt interaction", // Name of the test.
|
||||
&Input{ // An implementation of the survey.Prompt interface.
|
||||
Message: "What is your name?",
|
||||
},
|
||||
func(c *expect.Console) { // An expect procedure. You can expect strings / regexps and
|
||||
c.ExpectString("What is your name?") // write back strings / bytes to its psuedoterminal for survey.
|
||||
c.SendLine("Johnny Appleseed")
|
||||
c.ExpectEOF() // Nothing is read from the tty without an expect, and once an
|
||||
// expectation is met, no further bytes are read. End your
|
||||
// procedure with `c.ExpectEOF()` to read until survey finishes.
|
||||
},
|
||||
"Johnny Appleseed", // The expected result.
|
||||
}
|
||||
```
|
||||
|
||||
If you want to write your own `go-expect` test from scratch, you'll need to instantiate a virtual terminal,
|
||||
multiplex it into an `*expect.Console`, and hook up its tty with survey's optional stdio. Please see `go-expect`
|
||||
[documentation](https://godoc.org/github.com/Netflix/go-expect) for more detail.
|
78
vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock
generated
vendored
78
vendor/github.com/AlecAivazis/survey/v2/Gopkg.lock
generated
vendored
|
@ -1,78 +0,0 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Netflix/go-expect"
|
||||
packages = ["."]
|
||||
revision = "c93bf25de8e869da25cf26bcd2932b36141f61ae"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hinshun/vt10x"
|
||||
packages = ["."]
|
||||
revision = "1954e646417484a2a687ea344edade2c2b6523c8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kballard/go-shellquote"
|
||||
packages = ["."]
|
||||
revision = "95032a82bc518f77982ea72343cc1ade730072f0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kr/pty"
|
||||
packages = ["."]
|
||||
revision = "282ce0e5322c82529687d609ee670fac7c7d917c"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mgutz/ansi"
|
||||
packages = ["."]
|
||||
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "371508ebad4798adc38a118f858b5c17a65b58594203548f9feb74cb781dd907"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
54
vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml
generated
vendored
54
vendor/github.com/AlecAivazis/survey/v2/Gopkg.toml
generated
vendored
|
@ -1,54 +0,0 @@
|
|||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/Netflix/go-expect"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hinshun/vt10x"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
version = "0.0.3"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mgutz/ansi"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kballard/go-shellquote"
|
21
vendor/github.com/AlecAivazis/survey/v2/LICENSE
generated
vendored
21
vendor/github.com/AlecAivazis/survey/v2/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Alec Aivazis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
480
vendor/github.com/AlecAivazis/survey/v2/README.md
generated
vendored
480
vendor/github.com/AlecAivazis/survey/v2/README.md
generated
vendored
|
@ -1,480 +0,0 @@
|
|||
# Survey
|
||||
|
||||
[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
|
||||
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2)
|
||||
|
||||
A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.
|
||||
|
||||
<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
)
|
||||
|
||||
// the questions to ask
|
||||
var qs = []*survey.Question{
|
||||
{
|
||||
Name: "name",
|
||||
Prompt: &survey.Input{Message: "What is your name?"},
|
||||
Validate: survey.Required,
|
||||
Transform: survey.Title,
|
||||
},
|
||||
{
|
||||
Name: "color",
|
||||
Prompt: &survey.Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"red", "blue", "green"},
|
||||
Default: "red",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "age",
|
||||
Prompt: &survey.Input{Message: "How old are you?"},
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
// the answers will be written to this struct
|
||||
answers := struct {
|
||||
Name string // survey will match the question and field names
|
||||
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
|
||||
Age int // if the types don't match, survey will convert it
|
||||
}{}
|
||||
|
||||
// perform the questions
|
||||
err := survey.Ask(qs, &answers)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
|
||||
}
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Examples](#examples)
|
||||
1. [Running the Prompts](#running-the-prompts)
|
||||
1. [Prompts](#prompts)
|
||||
1. [Input](#input)
|
||||
1. [Suggestion Options](#suggestion-options)
|
||||
1. [Multiline](#multiline)
|
||||
1. [Password](#password)
|
||||
1. [Confirm](#confirm)
|
||||
1. [Select](#select)
|
||||
1. [MultiSelect](#multiselect)
|
||||
1. [Editor](#editor)
|
||||
1. [Filtering Options](#filtering-options)
|
||||
1. [Validation](#validation)
|
||||
1. [Built-in Validators](#built-in-validators)
|
||||
1. [Help Text](#help-text)
|
||||
1. [Changing the input rune](#changing-the-input-rune)
|
||||
1. [Changing the Icons ](#changing-the-icons)
|
||||
1. [Custom Types](#custom-types)
|
||||
1. [Testing](#testing)
|
||||
1. [FAQ](#faq)
|
||||
|
||||
## Examples
|
||||
|
||||
Examples can be found in the `examples/` directory. Run them
|
||||
to see basic behavior:
|
||||
|
||||
```bash
|
||||
go get github.com/AlecAivazis/survey/v2
|
||||
|
||||
cd $GOPATH/src/github.com/AlecAivazis/survey
|
||||
|
||||
go run examples/simple.go
|
||||
go run examples/validation.go
|
||||
```
|
||||
|
||||
## Running the Prompts
|
||||
|
||||
There are two primary ways to execute prompts and start collecting information from your users: `Ask` and
|
||||
`AskOne`. The primary difference is whether you are interested in collecting a single piece of information
|
||||
or if you have a list of questions to ask whose answers should be collected in a single struct.
|
||||
For most basic usecases, `Ask` should be enough. However, for surveys with complicated branching logic,
|
||||
we recommend that you break out your questions into multiple calls to both of these functions to fit your needs.
|
||||
|
||||
### Configuring the Prompts
|
||||
|
||||
Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also
|
||||
possible to change survey's default behaviors by passing `AskOpts` to either `Ask` or `AskOne`. Examples
|
||||
in this document will do both interchangeably:
|
||||
|
||||
```golang
|
||||
prompt := &Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"red", "blue", "green"},
|
||||
// can pass a validator directly
|
||||
Validate: survey.Required,
|
||||
}
|
||||
|
||||
// or define a default for the single call to `AskOne`
|
||||
// the answer will get written to the color variable
|
||||
survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
|
||||
|
||||
// or define a default for every entry in a list of questions
|
||||
// the answer will get copied into the matching field of the struct as shown above
|
||||
survey.Ask(questions, &answers, survey.WithValidator(survey.Required))
|
||||
```
|
||||
|
||||
## Prompts
|
||||
|
||||
### Input
|
||||
|
||||
<img src="https://thumbs.gfycat.com/LankyBlindAmericanpainthorse-size_restricted.gif" width="400px"/>
|
||||
|
||||
```golang
|
||||
name := ""
|
||||
prompt := &survey.Input{
|
||||
Message: "ping",
|
||||
}
|
||||
survey.AskOne(prompt, &name)
|
||||
```
|
||||
|
||||
#### Suggestion Options
|
||||
|
||||
<img src="https://i.imgur.com/Q7POpA1.gif" width="800px"/>
|
||||
|
||||
```golang
|
||||
file := ""
|
||||
prompt := &survey.Input{
|
||||
Message: "inform a file to save:",
|
||||
Suggest: func (toComplete string) []string {
|
||||
files, _ := filepath.Glob(toComplete + "*")
|
||||
return files
|
||||
},
|
||||
}
|
||||
}
|
||||
survey.AskOne(prompt, &file)
|
||||
```
|
||||
|
||||
### Multiline
|
||||
|
||||
<img src="https://thumbs.gfycat.com/ImperfectShimmeringBeagle-size_restricted.gif" width="400px"/>
|
||||
|
||||
```golang
|
||||
text := ""
|
||||
prompt := &survey.Multiline{
|
||||
Message: "ping",
|
||||
}
|
||||
survey.AskOne(prompt, &text)
|
||||
```
|
||||
|
||||
### Password
|
||||
|
||||
<img src="https://thumbs.gfycat.com/CompassionateSevereHypacrosaurus-size_restricted.gif" width="400px" />
|
||||
|
||||
```golang
|
||||
password := ""
|
||||
prompt := &survey.Password{
|
||||
Message: "Please type your password",
|
||||
}
|
||||
survey.AskOne(prompt, &password)
|
||||
```
|
||||
|
||||
### Confirm
|
||||
|
||||
<img src="https://thumbs.gfycat.com/UnkemptCarefulGermanpinscher-size_restricted.gif" width="400px"/>
|
||||
|
||||
```golang
|
||||
name := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: "Do you like pie?",
|
||||
}
|
||||
survey.AskOne(prompt, &name)
|
||||
```
|
||||
|
||||
### Select
|
||||
|
||||
<img src="https://thumbs.gfycat.com/GrimFilthyAmazonparrot-size_restricted.gif" width="450px"/>
|
||||
|
||||
```golang
|
||||
color := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"red", "blue", "green"},
|
||||
}
|
||||
survey.AskOne(prompt, &color)
|
||||
```
|
||||
|
||||
Fields and values that come from a `Select` prompt can be one of two different things. If you pass an `int`
|
||||
the field will have the value of the selected index. If you instead pass a string, the string value selected
|
||||
will be written to the field.
|
||||
|
||||
The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
|
||||
|
||||
By default, the select prompt is limited to showing 7 options at a time
|
||||
and will paginate lists of options longer than that. This can be changed a number of ways:
|
||||
|
||||
```golang
|
||||
// as a field on a single select
|
||||
prompt := &survey.MultiSelect{..., PageSize: 10}
|
||||
|
||||
// or as an option to Ask or AskOne
|
||||
survey.AskOne(prompt, &days, survey.WithPageSize(10))
|
||||
```
|
||||
|
||||
### MultiSelect
|
||||
|
||||
![Example](img/multi-select-all-none.gif)
|
||||
|
||||
```golang
|
||||
days := []string{}
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "What days do you prefer:",
|
||||
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
|
||||
}
|
||||
survey.AskOne(prompt, &days)
|
||||
```
|
||||
|
||||
Fields and values that come from a `MultiSelect` prompt can be one of two different things. If you pass an `int`
|
||||
the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values
|
||||
selected will be written to the field.
|
||||
|
||||
The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
|
||||
|
||||
By default, the MultiSelect prompt is limited to showing 7 options at a time
|
||||
and will paginate lists of options longer than that. This can be changed a number of ways:
|
||||
|
||||
```golang
|
||||
// as a field on a single select
|
||||
prompt := &survey.MultiSelect{..., PageSize: 10}
|
||||
|
||||
// or as an option to Ask or AskOne
|
||||
survey.AskOne(prompt, &days, survey.WithPageSize(10))
|
||||
```
|
||||
|
||||
### Editor
|
||||
|
||||
Launches the user's preferred editor (defined by the \$VISUAL or \$EDITOR environment variables) on a
|
||||
temporary file. Once the user exits their editor, the contents of the temporary file are read in as
|
||||
the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.
|
||||
|
||||
You can also specify a [pattern](https://golang.org/pkg/io/ioutil/#TempFile) for the name of the temporary file. This
|
||||
can be useful for ensuring syntax highlighting matches your usecase.
|
||||
|
||||
```golang
|
||||
prompt := &survey.Editor{
|
||||
Message: "Shell code snippet",
|
||||
FileName: "*.sh",
|
||||
}
|
||||
|
||||
survey.AskOne(prompt, &content)
|
||||
```
|
||||
|
||||
## Filtering Options
|
||||
|
||||
By default, the user can filter for options in Select and MultiSelects by typing while the prompt
|
||||
is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case.
|
||||
|
||||
A custom filter function can also be provided to change this behavior:
|
||||
|
||||
```golang
|
||||
func myFilter(filterValue string, optValue string, optIndex int) bool {
|
||||
// only include the option if it includes the filter and has length greater than 5
|
||||
return strings.Contains(optValue, filterValue) && len(optValue) >= 5
|
||||
}
|
||||
|
||||
// configure it for a specific prompt
|
||||
&Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"red", "blue", "green"},
|
||||
Filter: myFilter,
|
||||
}
|
||||
|
||||
// or define a default for all of the questions
|
||||
survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
|
||||
```
|
||||
|
||||
## Keeping the filter active
|
||||
|
||||
By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
|
||||
|
||||
However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
|
||||
|
||||
```golang
|
||||
// configure it for a specific prompt
|
||||
&Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"light-green", "green", "dark-green", "red"},
|
||||
KeepFilter: true,
|
||||
}
|
||||
|
||||
// or define a default for all of the questions
|
||||
survey.AskOne(prompt, &color, survey.WithKeepFilter(true))
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
Validating individual responses for a particular question can be done by defining a
|
||||
`Validate` field on the `survey.Question` to be validated. This function takes an
|
||||
`interface{}` type and returns an error to show to the user, prompting them for another
|
||||
response. Like usual, validators can be provided directly to the prompt or with `survey.WithValidator`:
|
||||
|
||||
```golang
|
||||
q := &survey.Question{
|
||||
Prompt: &survey.Input{Message: "Hello world validation"},
|
||||
Validate: func (val interface{}) error {
|
||||
// since we are validating an Input, the assertion will always succeed
|
||||
if str, ok := val.(string) ; !ok || len(str) > 10 {
|
||||
return errors.New("This response cannot be longer than 10 characters.")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
color := ""
|
||||
prompt := &survey.Input{ Message: "Whats your name?" }
|
||||
|
||||
// you can pass multiple validators here and survey will make sure each one passes
|
||||
survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
|
||||
```
|
||||
|
||||
### Built-in Validators
|
||||
|
||||
`survey` comes prepackaged with a few validators to fit common situations. Currently these
|
||||
validators include:
|
||||
|
||||
| name | valid types | description | notes |
|
||||
| ------------ | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
|
||||
| MinLength(n) | string | Enforces that a response is at least the given length | |
|
||||
| MaxLength(n) | string | Enforces that a response is no longer than the given length | |
|
||||
| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | |
|
||||
| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | |
|
||||
|
||||
## Help Text
|
||||
|
||||
All of the prompts have a `Help` field which can be defined to provide more information to your users:
|
||||
|
||||
<img src="https://thumbs.gfycat.com/CloudyRemorsefulFossa-size_restricted.gif" width="400px" style="margin-top: 8px"/>
|
||||
|
||||
```golang
|
||||
&survey.Input{
|
||||
Message: "What is your phone number:",
|
||||
Help: "Phone number should include the area code",
|
||||
}
|
||||
```
|
||||
|
||||
### Changing the input rune
|
||||
|
||||
In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
|
||||
looks for with `WithHelpInput`:
|
||||
|
||||
```golang
|
||||
import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
)
|
||||
|
||||
number := ""
|
||||
prompt := &survey.Input{
|
||||
Message: "If you have this need, please give me a reasonable message.",
|
||||
Help: "I couldn't come up with one.",
|
||||
}
|
||||
|
||||
survey.AskOne(prompt, &number, survey.WithHelpInput('^'))
|
||||
```
|
||||
|
||||
## Changing the Icons
|
||||
|
||||
Changing the icons and their color/format can be done by passing the `WithIcons` option. The format
|
||||
follows the patterns outlined [here](https://github.com/mgutz/ansi#style-format). For example:
|
||||
|
||||
```golang
|
||||
import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
)
|
||||
|
||||
number := ""
|
||||
prompt := &survey.Input{
|
||||
Message: "If you have this need, please give me a reasonable message.",
|
||||
Help: "I couldn't come up with one.",
|
||||
}
|
||||
|
||||
survey.AskOne(prompt, &number, survey.WithIcons(func(icons *survey.IconSet) {
|
||||
// you can set any icons
|
||||
icons.Question.Text = "⁇"
|
||||
// for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format
|
||||
icons.Question.Format = "yellow+hb"
|
||||
}))
|
||||
```
|
||||
|
||||
The icons and their default text and format are summarized below:
|
||||
|
||||
| name | text | format | description |
|
||||
| -------------- | ---- | ---------- | ------------------------------------------------------------- |
|
||||
| Error | X | red | Before an error |
|
||||
| Help | i | cyan | Before help text |
|
||||
| Question | ? | green+hb | Before the message of a prompt |
|
||||
| SelectFocus | > | green | Marks the current focus in `Select` and `MultiSelect` prompts |
|
||||
| UnmarkedOption | [ ] | default+hb | Marks an unselected option in a `MultiSelect` prompt |
|
||||
| MarkedOption | [x] | cyan+b | Marks a chosen selection in a `MultiSelect` prompt |
|
||||
|
||||
## Custom Types
|
||||
|
||||
survey will assign prompt answers to your custom types if they implement this interface:
|
||||
|
||||
```golang
|
||||
type Settable interface {
|
||||
WriteAnswer(field string, value interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example how to use them:
|
||||
|
||||
```golang
|
||||
type MyValue struct {
|
||||
value string
|
||||
}
|
||||
func (my *MyValue) WriteAnswer(name string, value interface{}) error {
|
||||
my.value = value.(string)
|
||||
}
|
||||
|
||||
myval := MyValue{}
|
||||
survey.AskOne(
|
||||
&survey.Input{
|
||||
Message: "Enter something:",
|
||||
},
|
||||
&myval
|
||||
)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library
|
||||
can be used to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY,
|
||||
if you are manipulating the cursor or using `survey`, you will need a way to interpret terminal / ANSI escape sequences
|
||||
for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes
|
||||
stdio to an in-memory [virtual terminal](https://github.com/hinshun/vt10x).
|
||||
|
||||
For some examples, you can see any of the tests in this repo.
|
||||
|
||||
## FAQ
|
||||
|
||||
### What kinds of IO are supported by `survey`?
|
||||
|
||||
survey aims to support most terminal emulators; it expects support for ANSI escape sequences.
|
||||
This means that reading from piped stdin or writing to piped stdout is **not supported**,
|
||||
and likely to break your application in these situations. See [#337](https://github.com/AlecAivazis/survey/pull/337#issue-581351617)
|
||||
|
||||
### Why isn't sending a SIGINT (aka. CTRL-C) signal working?
|
||||
|
||||
When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code:
|
||||
|
||||
```go
|
||||
err := survey.AskOne(prompt, &myVar)
|
||||
if err == terminal.InterruptErr {
|
||||
fmt.Println("interrupted")
|
||||
|
||||
os.Exit(0)
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
19
vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl
generated
vendored
19
vendor/github.com/AlecAivazis/survey/v2/_tasks.hcl
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
task "install-deps" {
|
||||
description = "Install all of package dependencies"
|
||||
pipeline = [
|
||||
"go get -v {{.files}}",
|
||||
]
|
||||
}
|
||||
|
||||
task "tests" {
|
||||
description = "Run the test suite"
|
||||
command = "go test -v {{.files}}"
|
||||
environment = {
|
||||
GOFLAGS = "-mod=vendor"
|
||||
}
|
||||
}
|
||||
|
||||
variables = {
|
||||
files = "$(go list -v ./... | grep -iEv \"tests|examples\")"
|
||||
}
|
||||
|
153
vendor/github.com/AlecAivazis/survey/v2/confirm.go
generated
vendored
153
vendor/github.com/AlecAivazis/survey/v2/confirm.go
generated
vendored
|
@ -1,153 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Confirm is a regular text input that accept yes/no answers. Response type is a bool.
|
||||
type Confirm struct {
|
||||
Renderer
|
||||
Message string
|
||||
Default bool
|
||||
Help string
|
||||
}
|
||||
|
||||
// data available to the templates when processing
|
||||
type ConfirmTemplateData struct {
|
||||
Confirm
|
||||
Answer string
|
||||
ShowHelp bool
|
||||
Config *PromptConfig
|
||||
}
|
||||
|
||||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
var ConfirmQuestionTemplate = `
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
|
||||
{{- if .Answer}}
|
||||
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
|
||||
{{- else }}
|
||||
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
|
||||
{{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
|
||||
{{- end}}`
|
||||
|
||||
// the regex for answers
|
||||
var (
|
||||
yesRx = regexp.MustCompile("^(?i:y(?:es)?)$")
|
||||
noRx = regexp.MustCompile("^(?i:n(?:o)?)$")
|
||||
)
|
||||
|
||||
func yesNo(t bool) string {
|
||||
if t {
|
||||
return "Yes"
|
||||
}
|
||||
return "No"
|
||||
}
|
||||
|
||||
func (c *Confirm) getBool(showHelp bool, config *PromptConfig) (bool, error) {
|
||||
cursor := c.NewCursor()
|
||||
rr := c.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
// start waiting for input
|
||||
for {
|
||||
line, err := rr.ReadLine(0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// move back up a line to compensate for the \n echoed from terminal
|
||||
cursor.PreviousLine(1)
|
||||
val := string(line)
|
||||
|
||||
// get the answer that matches the
|
||||
var answer bool
|
||||
switch {
|
||||
case yesRx.Match([]byte(val)):
|
||||
answer = true
|
||||
case noRx.Match([]byte(val)):
|
||||
answer = false
|
||||
case val == "":
|
||||
answer = c.Default
|
||||
case val == config.HelpInput && c.Help != "":
|
||||
err := c.Render(
|
||||
ConfirmQuestionTemplate,
|
||||
ConfirmTemplateData{
|
||||
Confirm: *c,
|
||||
ShowHelp: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// use the default value and bubble up
|
||||
return c.Default, err
|
||||
}
|
||||
showHelp = true
|
||||
continue
|
||||
default:
|
||||
// we didnt get a valid answer, so print error and prompt again
|
||||
if err := c.Error(config, fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil {
|
||||
return c.Default, err
|
||||
}
|
||||
err := c.Render(
|
||||
ConfirmQuestionTemplate,
|
||||
ConfirmTemplateData{
|
||||
Confirm: *c,
|
||||
ShowHelp: showHelp,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// use the default value and bubble up
|
||||
return c.Default, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return answer, nil
|
||||
}
|
||||
// should not get here
|
||||
return c.Default, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Prompt prompts the user with a simple text field and expects a reply followed
|
||||
by a carriage return.
|
||||
|
||||
likesPie := false
|
||||
prompt := &survey.Confirm{ Message: "What is your name?" }
|
||||
survey.AskOne(prompt, &likesPie)
|
||||
*/
|
||||
func (c *Confirm) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// render the question template
|
||||
err := c.Render(
|
||||
ConfirmQuestionTemplate,
|
||||
ConfirmTemplateData{
|
||||
Confirm: *c,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// get input and return
|
||||
return c.getBool(false, config)
|
||||
}
|
||||
|
||||
// Cleanup overwrite the line with the finalized formatted version
|
||||
func (c *Confirm) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
// if the value was previously true
|
||||
ans := yesNo(val.(bool))
|
||||
|
||||
// render the template
|
||||
return c.Render(
|
||||
ConfirmQuestionTemplate,
|
||||
ConfirmTemplateData{
|
||||
Confirm: *c,
|
||||
Answer: ans,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
}
|
91
vendor/github.com/AlecAivazis/survey/v2/core/template.go
generated
vendored
91
vendor/github.com/AlecAivazis/survey/v2/core/template.go
generated
vendored
|
@ -1,91 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/mgutz/ansi"
|
||||
)
|
||||
|
||||
// DisableColor can be used to make testing reliable
|
||||
var DisableColor = false
|
||||
|
||||
var TemplateFuncsWithColor = map[string]interface{}{
|
||||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
"color": ansi.ColorCode,
|
||||
}
|
||||
|
||||
var TemplateFuncsNoColor = map[string]interface{}{
|
||||
// Templates without Color formatting. For layout/ testing.
|
||||
"color": func(color string) string {
|
||||
return ""
|
||||
},
|
||||
}
|
||||
|
||||
//RunTemplate returns two formatted strings given a template and
|
||||
//the data it requires. The first string returned is generated for
|
||||
//user-facing output and may or may not contain ANSI escape codes
|
||||
//for colored output. The second string does not contain escape codes
|
||||
//and can be used by the renderer for layout purposes.
|
||||
func RunTemplate(tmpl string, data interface{}) (string, string, error) {
|
||||
tPair, err := GetTemplatePair(tmpl)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
userBuf := bytes.NewBufferString("")
|
||||
err = tPair[0].Execute(userBuf, data)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
layoutBuf := bytes.NewBufferString("")
|
||||
err = tPair[1].Execute(layoutBuf, data)
|
||||
if err != nil {
|
||||
return userBuf.String(), "", err
|
||||
}
|
||||
return userBuf.String(), layoutBuf.String(), err
|
||||
}
|
||||
|
||||
var (
|
||||
memoizedGetTemplate = map[string][2]*template.Template{}
|
||||
|
||||
memoMutex = &sync.RWMutex{}
|
||||
)
|
||||
|
||||
//GetTemplatePair returns a pair of compiled templates where the
|
||||
//first template is generated for user-facing output and the
|
||||
//second is generated for use by the renderer. The second
|
||||
//template does not contain any color escape codes, whereas
|
||||
//the first template may or may not depending on DisableColor.
|
||||
func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
|
||||
memoMutex.RLock()
|
||||
if t, ok := memoizedGetTemplate[tmpl]; ok {
|
||||
memoMutex.RUnlock()
|
||||
return t, nil
|
||||
}
|
||||
memoMutex.RUnlock()
|
||||
|
||||
templatePair := [2]*template.Template{nil, nil}
|
||||
|
||||
templateNoColor, err := template.New("prompt").Funcs(TemplateFuncsNoColor).Parse(tmpl)
|
||||
if err != nil {
|
||||
return [2]*template.Template{}, err
|
||||
}
|
||||
|
||||
templatePair[1] = templateNoColor
|
||||
|
||||
if DisableColor {
|
||||
templatePair[0] = templatePair[1]
|
||||
} else {
|
||||
templateWithColor, err := template.New("prompt").Funcs(TemplateFuncsWithColor).Parse(tmpl)
|
||||
templatePair[0] = templateWithColor
|
||||
if err != nil {
|
||||
return [2]*template.Template{}, err
|
||||
}
|
||||
}
|
||||
|
||||
memoMutex.Lock()
|
||||
memoizedGetTemplate[tmpl] = templatePair
|
||||
memoMutex.Unlock()
|
||||
return templatePair, nil
|
||||
}
|
374
vendor/github.com/AlecAivazis/survey/v2/core/write.go
generated
vendored
374
vendor/github.com/AlecAivazis/survey/v2/core/write.go
generated
vendored
|
@ -1,374 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// the tag used to denote the name of the question
|
||||
const tagName = "survey"
|
||||
|
||||
// Settable allow for configuration when assigning answers
|
||||
type Settable interface {
|
||||
WriteAnswer(field string, value interface{}) error
|
||||
}
|
||||
|
||||
// OptionAnswer is the return type of Selects/MultiSelects that lets the appropriate information
|
||||
// get copied to the user's struct
|
||||
type OptionAnswer struct {
|
||||
Value string
|
||||
Index int
|
||||
}
|
||||
|
||||
type reflectField struct {
|
||||
value reflect.Value
|
||||
fieldType reflect.StructField
|
||||
}
|
||||
|
||||
func OptionAnswerList(incoming []string) []OptionAnswer {
|
||||
list := []OptionAnswer{}
|
||||
for i, opt := range incoming {
|
||||
list = append(list, OptionAnswer{Value: opt, Index: i})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
|
||||
// if the field is a custom type
|
||||
if s, ok := t.(Settable); ok {
|
||||
// use the interface method
|
||||
return s.WriteAnswer(name, v)
|
||||
}
|
||||
|
||||
// the target to write to
|
||||
target := reflect.ValueOf(t)
|
||||
// the value to write from
|
||||
value := reflect.ValueOf(v)
|
||||
|
||||
// make sure we are writing to a pointer
|
||||
if target.Kind() != reflect.Ptr {
|
||||
return errors.New("you must pass a pointer as the target of a Write operation")
|
||||
}
|
||||
// the object "inside" of the target pointer
|
||||
elem := target.Elem()
|
||||
|
||||
// handle the special types
|
||||
switch elem.Kind() {
|
||||
// if we are writing to a struct
|
||||
case reflect.Struct:
|
||||
// if we are writing to an option answer than we want to treat
|
||||
// it like a single thing and not a place to deposit answers
|
||||
if elem.Type().Name() == "OptionAnswer" {
|
||||
// copy the value over to the normal struct
|
||||
return copy(elem, value)
|
||||
}
|
||||
|
||||
// get the name of the field that matches the string we were given
|
||||
field, _, err := findField(elem, name)
|
||||
// if something went wrong
|
||||
if err != nil {
|
||||
// bubble up
|
||||
return err
|
||||
}
|
||||
// handle references to the Settable interface aswell
|
||||
if s, ok := field.Interface().(Settable); ok {
|
||||
// use the interface method
|
||||
return s.WriteAnswer(name, v)
|
||||
}
|
||||
if field.CanAddr() {
|
||||
if s, ok := field.Addr().Interface().(Settable); ok {
|
||||
// use the interface method
|
||||
return s.WriteAnswer(name, v)
|
||||
}
|
||||
}
|
||||
|
||||
// copy the value over to the normal struct
|
||||
return copy(field, value)
|
||||
case reflect.Map:
|
||||
mapType := reflect.TypeOf(t).Elem()
|
||||
if mapType.Key().Kind() != reflect.String {
|
||||
return errors.New("answer maps key must be of type string")
|
||||
}
|
||||
|
||||
// copy only string value/index value to map if,
|
||||
// map is not of type interface and is 'OptionAnswer'
|
||||
if value.Type().Name() == "OptionAnswer" {
|
||||
if kval := mapType.Elem().Kind(); kval == reflect.String {
|
||||
mt := *t.(*map[string]string)
|
||||
mt[name] = value.FieldByName("Value").String()
|
||||
return nil
|
||||
} else if kval == reflect.Int {
|
||||
mt := *t.(*map[string]int)
|
||||
mt[name] = int(value.FieldByName("Index").Int())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if mapType.Elem().Kind() != reflect.Interface {
|
||||
return errors.New("answer maps must be of type map[string]interface")
|
||||
}
|
||||
mt := *t.(*map[string]interface{})
|
||||
mt[name] = value.Interface()
|
||||
return nil
|
||||
}
|
||||
// otherwise just copy the value to the target
|
||||
return copy(elem, value)
|
||||
}
|
||||
|
||||
type errFieldNotMatch struct {
|
||||
questionName string
|
||||
}
|
||||
|
||||
func (err errFieldNotMatch) Error() string {
|
||||
return fmt.Sprintf("could not find field matching %v", err.questionName)
|
||||
}
|
||||
|
||||
func (err errFieldNotMatch) Is(target error) bool { // implements the dynamic errors.Is interface.
|
||||
if target != nil {
|
||||
if name, ok := IsFieldNotMatch(target); ok {
|
||||
// if have a filled questionName then perform "deeper" comparison.
|
||||
return name == "" || err.questionName == "" || name == err.questionName
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFieldNotMatch reports whether an "err" is caused by a non matching field.
|
||||
// It returns the Question.Name that couldn't be matched with a destination field.
|
||||
//
|
||||
// Usage:
|
||||
// err := survey.Ask(qs, &v);
|
||||
// if err != nil {
|
||||
// if name, ok := core.IsFieldNotMatch(err); ok {
|
||||
// [...name is the not matched question name]
|
||||
// }
|
||||
// }
|
||||
func IsFieldNotMatch(err error) (string, bool) {
|
||||
if err != nil {
|
||||
if v, ok := err.(errFieldNotMatch); ok {
|
||||
return v.questionName, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are
|
||||
// two fields with same name that only differ by casing.
|
||||
func findField(s reflect.Value, name string) (reflect.Value, reflect.StructField, error) {
|
||||
|
||||
fields := flattenFields(s)
|
||||
|
||||
// first look for matching tags so we can overwrite matching field names
|
||||
for _, f := range fields {
|
||||
// the value of the survey tag
|
||||
tag := f.fieldType.Tag.Get(tagName)
|
||||
// if the tag matches the name we are looking for
|
||||
if tag != "" && tag == name {
|
||||
// then we found our index
|
||||
return f.value, f.fieldType, nil
|
||||
}
|
||||
}
|
||||
|
||||
// then look for matching names
|
||||
for _, f := range fields {
|
||||
// if the name of the field matches what we're looking for
|
||||
if strings.ToLower(f.fieldType.Name) == strings.ToLower(name) {
|
||||
return f.value, f.fieldType, nil
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find the field
|
||||
return reflect.Value{}, reflect.StructField{}, errFieldNotMatch{name}
|
||||
}
|
||||
|
||||
func flattenFields(s reflect.Value) []reflectField {
|
||||
sType := s.Type()
|
||||
numField := sType.NumField()
|
||||
fields := make([]reflectField, 0, numField)
|
||||
for i := 0; i < numField; i++ {
|
||||
fieldType := sType.Field(i)
|
||||
field := s.Field(i)
|
||||
|
||||
if field.Kind() == reflect.Struct && fieldType.Anonymous {
|
||||
// field is a promoted structure
|
||||
for _, f := range flattenFields(field) {
|
||||
fields = append(fields, f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
fields = append(fields, reflectField{field, fieldType})
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// isList returns true if the element is something we can Len()
|
||||
func isList(v reflect.Value) bool {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Write takes a value and copies it to the target
|
||||
func copy(t reflect.Value, v reflect.Value) (err error) {
|
||||
// if something ends up panicing we need to catch it in a deferred func
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// if we paniced with an error
|
||||
if _, ok := r.(error); ok {
|
||||
// cast the result to an error object
|
||||
err = r.(error)
|
||||
} else if _, ok := r.(string); ok {
|
||||
// otherwise we could have paniced with a string so wrap it in an error
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// if we are copying from a string result to something else
|
||||
if v.Kind() == reflect.String && v.Type() != t.Type() {
|
||||
var castVal interface{}
|
||||
var casterr error
|
||||
vString := v.Interface().(string)
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
castVal, casterr = strconv.ParseBool(vString)
|
||||
case reflect.Int:
|
||||
castVal, casterr = strconv.Atoi(vString)
|
||||
case reflect.Int8:
|
||||
var val64 int64
|
||||
val64, casterr = strconv.ParseInt(vString, 10, 8)
|
||||
if casterr == nil {
|
||||
castVal = int8(val64)
|
||||
}
|
||||
case reflect.Int16:
|
||||
var val64 int64
|
||||
val64, casterr = strconv.ParseInt(vString, 10, 16)
|
||||
if casterr == nil {
|
||||
castVal = int16(val64)
|
||||
}
|
||||
case reflect.Int32:
|
||||
var val64 int64
|
||||
val64, casterr = strconv.ParseInt(vString, 10, 32)
|
||||
if casterr == nil {
|
||||
castVal = int32(val64)
|
||||
}
|
||||
case reflect.Int64:
|
||||
if t.Type() == reflect.TypeOf(time.Duration(0)) {
|
||||
castVal, casterr = time.ParseDuration(vString)
|
||||
} else {
|
||||
castVal, casterr = strconv.ParseInt(vString, 10, 64)
|
||||
}
|
||||
case reflect.Uint:
|
||||
var val64 uint64
|
||||
val64, casterr = strconv.ParseUint(vString, 10, 8)
|
||||
if casterr == nil {
|
||||
castVal = uint(val64)
|
||||
}
|
||||
case reflect.Uint8:
|
||||
var val64 uint64
|
||||
val64, casterr = strconv.ParseUint(vString, 10, 8)
|
||||
if casterr == nil {
|
||||
castVal = uint8(val64)
|
||||
}
|
||||
case reflect.Uint16:
|
||||
var val64 uint64
|
||||
val64, casterr = strconv.ParseUint(vString, 10, 16)
|
||||
if casterr == nil {
|
||||
castVal = uint16(val64)
|
||||
}
|
||||
case reflect.Uint32:
|
||||
var val64 uint64
|
||||
val64, casterr = strconv.ParseUint(vString, 10, 32)
|
||||
if casterr == nil {
|
||||
castVal = uint32(val64)
|
||||
}
|
||||
case reflect.Uint64:
|
||||
castVal, casterr = strconv.ParseUint(vString, 10, 64)
|
||||
case reflect.Float32:
|
||||
var val64 float64
|
||||
val64, casterr = strconv.ParseFloat(vString, 32)
|
||||
if casterr == nil {
|
||||
castVal = float32(val64)
|
||||
}
|
||||
case reflect.Float64:
|
||||
castVal, casterr = strconv.ParseFloat(vString, 64)
|
||||
default:
|
||||
return fmt.Errorf("Unable to convert from string to type %s", t.Kind())
|
||||
}
|
||||
|
||||
if casterr != nil {
|
||||
return casterr
|
||||
}
|
||||
|
||||
t.Set(reflect.ValueOf(castVal))
|
||||
return
|
||||
}
|
||||
|
||||
// if we are copying from an OptionAnswer to something
|
||||
if v.Type().Name() == "OptionAnswer" {
|
||||
// copying an option answer to a string
|
||||
if t.Kind() == reflect.String {
|
||||
// copies the Value field of the struct
|
||||
t.Set(reflect.ValueOf(v.FieldByName("Value").Interface()))
|
||||
return
|
||||
}
|
||||
|
||||
// copying an option answer to an int
|
||||
if t.Kind() == reflect.Int {
|
||||
// copies the Index field of the struct
|
||||
t.Set(reflect.ValueOf(v.FieldByName("Index").Interface()))
|
||||
return
|
||||
}
|
||||
|
||||
// copying an OptionAnswer to an OptionAnswer
|
||||
if t.Type().Name() == "OptionAnswer" {
|
||||
t.Set(v)
|
||||
return
|
||||
}
|
||||
|
||||
// we're copying an option answer to an incorrect type
|
||||
return fmt.Errorf("Unable to convert from OptionAnswer to type %s", t.Kind())
|
||||
}
|
||||
|
||||
// if we are copying from one slice or array to another
|
||||
if isList(v) && isList(t) {
|
||||
// loop over every item in the desired value
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
// write to the target given its kind
|
||||
switch t.Kind() {
|
||||
// if its a slice
|
||||
case reflect.Slice:
|
||||
// an object of the correct type
|
||||
obj := reflect.Indirect(reflect.New(t.Type().Elem()))
|
||||
|
||||
// write the appropriate value to the obj and catch any errors
|
||||
if err := copy(obj, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// just append the value to the end
|
||||
t.Set(reflect.Append(t, obj))
|
||||
// otherwise it could be an array
|
||||
case reflect.Array:
|
||||
// set the index to the appropriate value
|
||||
copy(t.Slice(i, i+1).Index(0), v.Index(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// set the value to the target
|
||||
t.Set(v)
|
||||
}
|
||||
|
||||
// we're done
|
||||
return
|
||||
}
|
222
vendor/github.com/AlecAivazis/survey/v2/editor.go
generated
vendored
222
vendor/github.com/AlecAivazis/survey/v2/editor.go
generated
vendored
|
@ -1,222 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
shellquote "github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
/*
|
||||
Editor launches an instance of the users preferred editor on a temporary file.
|
||||
The editor to use is determined by reading the $VISUAL or $EDITOR environment
|
||||
variables. If neither of those are present, notepad (on Windows) or vim
|
||||
(others) is used.
|
||||
The launch of the editor is triggered by the enter key. Since the response may
|
||||
be long, it will not be echoed as Input does, instead, it print <Received>.
|
||||
Response type is a string.
|
||||
|
||||
message := ""
|
||||
prompt := &survey.Editor{ Message: "What is your commit message?" }
|
||||
survey.AskOne(prompt, &message)
|
||||
*/
|
||||
type Editor struct {
|
||||
Renderer
|
||||
Message string
|
||||
Default string
|
||||
Help string
|
||||
Editor string
|
||||
HideDefault bool
|
||||
AppendDefault bool
|
||||
FileName string
|
||||
}
|
||||
|
||||
// data available to the templates when processing
|
||||
type EditorTemplateData struct {
|
||||
Editor
|
||||
Answer string
|
||||
ShowAnswer bool
|
||||
ShowHelp bool
|
||||
Config *PromptConfig
|
||||
}
|
||||
|
||||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
var EditorQuestionTemplate = `
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
|
||||
{{- if .ShowAnswer}}
|
||||
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
|
||||
{{- else }}
|
||||
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
|
||||
{{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
|
||||
{{- color "cyan"}}[Enter to launch editor] {{color "reset"}}
|
||||
{{- end}}`
|
||||
|
||||
var (
|
||||
bom = []byte{0xef, 0xbb, 0xbf}
|
||||
editor = "vim"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "windows" {
|
||||
editor = "notepad"
|
||||
}
|
||||
if v := os.Getenv("VISUAL"); v != "" {
|
||||
editor = v
|
||||
} else if e := os.Getenv("EDITOR"); e != "" {
|
||||
editor = e
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Editor) PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error) {
|
||||
initialValue := invalid.(string)
|
||||
return e.prompt(initialValue, config)
|
||||
}
|
||||
|
||||
func (e *Editor) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
initialValue := ""
|
||||
if e.Default != "" && e.AppendDefault {
|
||||
initialValue = e.Default
|
||||
}
|
||||
return e.prompt(initialValue, config)
|
||||
}
|
||||
|
||||
func (e *Editor) prompt(initialValue string, config *PromptConfig) (interface{}, error) {
|
||||
// render the template
|
||||
err := e.Render(
|
||||
EditorQuestionTemplate,
|
||||
EditorTemplateData{
|
||||
Editor: *e,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// start reading runes from the standard in
|
||||
rr := e.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
cursor := e.NewCursor()
|
||||
cursor.Hide()
|
||||
defer cursor.Show()
|
||||
|
||||
for {
|
||||
r, _, err := rr.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r == '\r' || r == '\n' {
|
||||
break
|
||||
}
|
||||
if r == terminal.KeyInterrupt {
|
||||
return "", terminal.InterruptErr
|
||||
}
|
||||
if r == terminal.KeyEndTransmission {
|
||||
break
|
||||
}
|
||||
if string(r) == config.HelpInput && e.Help != "" {
|
||||
err = e.Render(
|
||||
EditorQuestionTemplate,
|
||||
EditorTemplateData{
|
||||
Editor: *e,
|
||||
ShowHelp: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// prepare the temp file
|
||||
pattern := e.FileName
|
||||
if pattern == "" {
|
||||
pattern = "survey*.txt"
|
||||
}
|
||||
f, err := ioutil.TempFile("", pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
// write utf8 BOM header
|
||||
// The reason why we do this is because notepad.exe on Windows determines the
|
||||
// encoding of an "empty" text file by the locale, for example, GBK in China,
|
||||
// while golang string only handles utf8 well. However, a text file with utf8
|
||||
// BOM header is not considered "empty" on Windows, and the encoding will then
|
||||
// be determined utf8 by notepad.exe, instead of GBK or other encodings.
|
||||
if _, err := f.Write(bom); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// write initial value
|
||||
if _, err := f.WriteString(initialValue); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// close the fd to prevent the editor unable to save file
|
||||
if err := f.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// check is input editor exist
|
||||
if e.Editor != "" {
|
||||
editor = e.Editor
|
||||
}
|
||||
|
||||
stdio := e.Stdio()
|
||||
|
||||
args, err := shellquote.Split(editor)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
args = append(args, f.Name())
|
||||
|
||||
// open the editor
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdin = stdio.In
|
||||
cmd.Stdout = stdio.Out
|
||||
cmd.Stderr = stdio.Err
|
||||
cursor.Show()
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// raw is a BOM-unstripped UTF8 byte slice
|
||||
raw, err := ioutil.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// strip BOM header
|
||||
text := string(bytes.TrimPrefix(raw, bom))
|
||||
|
||||
// check length, return default value on empty
|
||||
if len(text) == 0 && !e.AppendDefault {
|
||||
return e.Default, nil
|
||||
}
|
||||
|
||||
return text, nil
|
||||
}
|
||||
|
||||
func (e *Editor) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
return e.Render(
|
||||
EditorQuestionTemplate,
|
||||
EditorTemplateData{
|
||||
Editor: *e,
|
||||
Answer: "<Received>",
|
||||
ShowAnswer: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
}
|
1
vendor/github.com/AlecAivazis/survey/v2/filter.go
generated
vendored
1
vendor/github.com/AlecAivazis/survey/v2/filter.go
generated
vendored
|
@ -1 +0,0 @@
|
|||
package survey
|
19
vendor/github.com/AlecAivazis/survey/v2/go.mod
generated
vendored
19
vendor/github.com/AlecAivazis/survey/v2/go.mod
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
module github.com/AlecAivazis/survey/v2
|
||||
|
||||
require (
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/kr/pty v1.1.4
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.8
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.1
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56
|
||||
golang.org/x/text v0.3.3
|
||||
)
|
||||
|
||||
go 1.13
|
35
vendor/github.com/AlecAivazis/survey/v2/go.sum
generated
vendored
35
vendor/github.com/AlecAivazis/survey/v2/go.sum
generated
vendored
|
@ -1,35 +0,0 @@
|
|||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
225
vendor/github.com/AlecAivazis/survey/v2/input.go
generated
vendored
225
vendor/github.com/AlecAivazis/survey/v2/input.go
generated
vendored
|
@ -1,225 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
/*
|
||||
Input is a regular text input that prints each character the user types on the screen
|
||||
and accepts the input with the enter key. Response type is a string.
|
||||
|
||||
name := ""
|
||||
prompt := &survey.Input{ Message: "What is your name?" }
|
||||
survey.AskOne(prompt, &name)
|
||||
*/
|
||||
type Input struct {
|
||||
Renderer
|
||||
Message string
|
||||
Default string
|
||||
Help string
|
||||
Suggest func(toComplete string) []string
|
||||
answer string
|
||||
typedAnswer string
|
||||
options []core.OptionAnswer
|
||||
selectedIndex int
|
||||
showingHelp bool
|
||||
}
|
||||
|
||||
// data available to the templates when processing
|
||||
type InputTemplateData struct {
|
||||
Input
|
||||
ShowAnswer bool
|
||||
ShowHelp bool
|
||||
Answer string
|
||||
PageEntries []core.OptionAnswer
|
||||
SelectedIndex int
|
||||
Config *PromptConfig
|
||||
}
|
||||
|
||||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
var InputQuestionTemplate = `
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
|
||||
{{- if .ShowAnswer}}
|
||||
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
|
||||
{{- else if .PageEntries -}}
|
||||
{{- .Answer}} [Use arrows to move, enter to select, type to continue]
|
||||
{{- "\n"}}
|
||||
{{- range $ix, $choice := .PageEntries}}
|
||||
{{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
|
||||
{{- $choice.Value}}
|
||||
{{- color "reset"}}{{"\n"}}
|
||||
{{- end}}
|
||||
{{- else }}
|
||||
{{- if or (and .Help (not .ShowHelp)) .Suggest }}{{color "cyan"}}[
|
||||
{{- if and .Help (not .ShowHelp)}}{{ print .Config.HelpInput }} for help {{- if and .Suggest}}, {{end}}{{end -}}
|
||||
{{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}}
|
||||
]{{color "reset"}} {{end}}
|
||||
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
|
||||
{{- end}}`
|
||||
|
||||
func (i *Input) onRune(config *PromptConfig) terminal.OnRuneFn {
|
||||
return terminal.OnRuneFn(func(key rune, line []rune) ([]rune, bool, error) {
|
||||
if i.options != nil && (key == terminal.KeyEnter || key == '\n') {
|
||||
return []rune(i.answer), true, nil
|
||||
} else if i.options != nil && key == terminal.KeyEscape {
|
||||
i.answer = i.typedAnswer
|
||||
i.options = nil
|
||||
} else if key == terminal.KeyArrowUp && len(i.options) > 0 {
|
||||
if i.selectedIndex == 0 {
|
||||
i.selectedIndex = len(i.options) - 1
|
||||
} else {
|
||||
i.selectedIndex--
|
||||
}
|
||||
i.answer = i.options[i.selectedIndex].Value
|
||||
} else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 {
|
||||
if i.selectedIndex == len(i.options)-1 {
|
||||
i.selectedIndex = 0
|
||||
} else {
|
||||
i.selectedIndex++
|
||||
}
|
||||
i.answer = i.options[i.selectedIndex].Value
|
||||
} else if key == terminal.KeyTab && i.Suggest != nil {
|
||||
i.answer = string(line)
|
||||
i.typedAnswer = i.answer
|
||||
options := i.Suggest(i.answer)
|
||||
i.selectedIndex = 0
|
||||
if len(options) == 0 {
|
||||
return line, false, nil
|
||||
}
|
||||
|
||||
i.answer = options[0]
|
||||
if len(options) == 1 {
|
||||
i.typedAnswer = i.answer
|
||||
i.options = nil
|
||||
} else {
|
||||
i.options = core.OptionAnswerList(options)
|
||||
}
|
||||
} else {
|
||||
if i.options == nil {
|
||||
return line, false, nil
|
||||
}
|
||||
|
||||
if key >= terminal.KeySpace {
|
||||
i.answer += string(key)
|
||||
}
|
||||
i.typedAnswer = i.answer
|
||||
|
||||
i.options = nil
|
||||
}
|
||||
|
||||
pageSize := config.PageSize
|
||||
opts, idx := paginate(pageSize, i.options, i.selectedIndex)
|
||||
err := i.Render(
|
||||
InputQuestionTemplate,
|
||||
InputTemplateData{
|
||||
Input: *i,
|
||||
Answer: i.answer,
|
||||
ShowHelp: i.showingHelp,
|
||||
SelectedIndex: idx,
|
||||
PageEntries: opts,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
err = readLineAgain
|
||||
}
|
||||
|
||||
return []rune(i.typedAnswer), true, err
|
||||
})
|
||||
}
|
||||
|
||||
var readLineAgain = errors.New("read line again")
|
||||
|
||||
func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// render the template
|
||||
err := i.Render(
|
||||
InputQuestionTemplate,
|
||||
InputTemplateData{
|
||||
Input: *i,
|
||||
Config: config,
|
||||
ShowHelp: i.showingHelp,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// start reading runes from the standard in
|
||||
rr := i.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
cursor := i.NewCursor()
|
||||
if !config.ShowCursor {
|
||||
cursor.Hide() // hide the cursor
|
||||
defer cursor.Show() // show the cursor when we're done
|
||||
}
|
||||
|
||||
var line []rune
|
||||
|
||||
for {
|
||||
if i.options != nil {
|
||||
line = []rune{}
|
||||
}
|
||||
|
||||
line, err = rr.ReadLineWithDefault(0, line, i.onRune(config))
|
||||
if err == readLineAgain {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
i.answer = string(line)
|
||||
// readline print an empty line, go up before we render the follow up
|
||||
cursor.Up(1)
|
||||
|
||||
// if we ran into the help string
|
||||
if i.answer == config.HelpInput && i.Help != "" {
|
||||
// show the help and prompt again
|
||||
i.showingHelp = true
|
||||
return i.Prompt(config)
|
||||
}
|
||||
|
||||
// if the line is empty
|
||||
if len(i.answer) == 0 {
|
||||
// use the default value
|
||||
return i.Default, err
|
||||
}
|
||||
|
||||
lineStr := i.answer
|
||||
|
||||
i.AppendRenderedText(lineStr)
|
||||
|
||||
// we're done
|
||||
return lineStr, err
|
||||
}
|
||||
|
||||
func (i *Input) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
// use the default answer when cleaning up the prompt if necessary
|
||||
ans := i.answer
|
||||
if ans == "" && i.Default != "" {
|
||||
ans = i.Default
|
||||
}
|
||||
|
||||
// render the cleanup
|
||||
return i.Render(
|
||||
InputQuestionTemplate,
|
||||
InputTemplateData{
|
||||
Input: *i,
|
||||
ShowAnswer: true,
|
||||
Config: config,
|
||||
Answer: ans,
|
||||
},
|
||||
)
|
||||
}
|
110
vendor/github.com/AlecAivazis/survey/v2/multiline.go
generated
vendored
110
vendor/github.com/AlecAivazis/survey/v2/multiline.go
generated
vendored
|
@ -1,110 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
type Multiline struct {
|
||||
Renderer
|
||||
Message string
|
||||
Default string
|
||||
Help string
|
||||
}
|
||||
|
||||
// data available to the templates when processing
|
||||
type MultilineTemplateData struct {
|
||||
Multiline
|
||||
Answer string
|
||||
ShowAnswer bool
|
||||
ShowHelp bool
|
||||
Config *PromptConfig
|
||||
}
|
||||
|
||||
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
var MultilineQuestionTemplate = `
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
|
||||
{{- if .ShowAnswer}}
|
||||
{{- "\n"}}{{color "cyan"}}{{.Answer}}{{color "reset"}}
|
||||
{{- if .Answer }}{{ "\n" }}{{ end }}
|
||||
{{- else }}
|
||||
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
|
||||
{{- color "cyan"}}[Enter 2 empty lines to finish]{{color "reset"}}
|
||||
{{- end}}`
|
||||
|
||||
func (i *Multiline) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// render the template
|
||||
err := i.Render(
|
||||
MultilineQuestionTemplate,
|
||||
MultilineTemplateData{
|
||||
Multiline: *i,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// start reading runes from the standard in
|
||||
rr := i.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
cursor := i.NewCursor()
|
||||
|
||||
multiline := make([]string, 0)
|
||||
|
||||
emptyOnce := false
|
||||
// get the next line
|
||||
for {
|
||||
line := []rune{}
|
||||
line, err = rr.ReadLine(0)
|
||||
if err != nil {
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
if string(line) == "" {
|
||||
if emptyOnce {
|
||||
numLines := len(multiline) + 2
|
||||
cursor.PreviousLine(numLines)
|
||||
for j := 0; j < numLines; j++ {
|
||||
terminal.EraseLine(i.Stdio().Out, terminal.ERASE_LINE_ALL)
|
||||
cursor.NextLine(1)
|
||||
}
|
||||
cursor.PreviousLine(numLines)
|
||||
break
|
||||
}
|
||||
emptyOnce = true
|
||||
} else {
|
||||
emptyOnce = false
|
||||
}
|
||||
multiline = append(multiline, string(line))
|
||||
}
|
||||
|
||||
val := strings.Join(multiline, "\n")
|
||||
val = strings.TrimSpace(val)
|
||||
|
||||
// if the line is empty
|
||||
if len(val) == 0 {
|
||||
// use the default value
|
||||
return i.Default, err
|
||||
}
|
||||
|
||||
i.AppendRenderedText(val)
|
||||
return val, err
|
||||
}
|
||||
|
||||
func (i *Multiline) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
return i.Render(
|
||||
MultilineQuestionTemplate,
|
||||
MultilineTemplateData{
|
||||
Multiline: *i,
|
||||
Answer: val.(string),
|
||||
ShowAnswer: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
}
|
346
vendor/github.com/AlecAivazis/survey/v2/multiselect.go
generated
vendored
346
vendor/github.com/AlecAivazis/survey/v2/multiselect.go
generated
vendored
|
@ -1,346 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
/*
|
||||
MultiSelect is a prompt that presents a list of various options to the user
|
||||
for them to select using the arrow keys and enter. Response type is a slice of strings.
|
||||
|
||||
days := []string{}
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "What days do you prefer:",
|
||||
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
|
||||
}
|
||||
survey.AskOne(prompt, &days)
|
||||
*/
|
||||
type MultiSelect struct {
|
||||
Renderer
|
||||
Message string
|
||||
Options []string
|
||||
Default interface{}
|
||||
Help string
|
||||
PageSize int
|
||||
VimMode bool
|
||||
FilterMessage string
|
||||
Filter func(filter string, value string, index int) bool
|
||||
filter string
|
||||
selectedIndex int
|
||||
checked map[int]bool
|
||||
showingHelp bool
|
||||
}
|
||||
|
||||
// data available to the templates when processing
|
||||
type MultiSelectTemplateData struct {
|
||||
MultiSelect
|
||||
Answer string
|
||||
ShowAnswer bool
|
||||
Checked map[int]bool
|
||||
SelectedIndex int
|
||||
ShowHelp bool
|
||||
PageEntries []core.OptionAnswer
|
||||
Config *PromptConfig
|
||||
|
||||
// These fields are used when rendering an individual option
|
||||
CurrentOpt core.OptionAnswer
|
||||
CurrentIndex int
|
||||
}
|
||||
|
||||
// IterateOption sets CurrentOpt and CurrentIndex appropriately so a multiselect option can be rendered individually
|
||||
func (m MultiSelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} {
|
||||
copy := m
|
||||
copy.CurrentIndex = ix
|
||||
copy.CurrentOpt = opt
|
||||
return copy
|
||||
}
|
||||
|
||||
var MultiSelectQuestionTemplate = `
|
||||
{{- define "option"}}
|
||||
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}}
|
||||
{{- if index .Checked .CurrentOpt.Index }}{{color .Config.Icons.MarkedOption.Format }} {{ .Config.Icons.MarkedOption.Text }} {{else}}{{color .Config.Icons.UnmarkedOption.Format }} {{ .Config.Icons.UnmarkedOption.Text }} {{end}}
|
||||
{{- color "reset"}}
|
||||
{{- " "}}{{- .CurrentOpt.Value}}
|
||||
{{end}}
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
|
||||
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
|
||||
{{- else }}
|
||||
{{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
|
||||
{{- "\n"}}
|
||||
{{- range $ix, $option := .PageEntries}}
|
||||
{{- template "option" $.IterateOption $ix $option}}
|
||||
{{- end}}
|
||||
{{- end}}`
|
||||
|
||||
// OnChange is called on every keypress.
|
||||
func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
|
||||
options := m.filterOptions(config)
|
||||
oldFilter := m.filter
|
||||
|
||||
if key == terminal.KeyArrowUp || (m.VimMode && key == 'k') {
|
||||
// if we are at the top of the list
|
||||
if m.selectedIndex == 0 {
|
||||
// go to the bottom
|
||||
m.selectedIndex = len(options) - 1
|
||||
} else {
|
||||
// decrement the selected index
|
||||
m.selectedIndex--
|
||||
}
|
||||
} else if key == terminal.KeyTab || key == terminal.KeyArrowDown || (m.VimMode && key == 'j') {
|
||||
// if we are at the bottom of the list
|
||||
if m.selectedIndex == len(options)-1 {
|
||||
// start at the top
|
||||
m.selectedIndex = 0
|
||||
} else {
|
||||
// increment the selected index
|
||||
m.selectedIndex++
|
||||
}
|
||||
// if the user pressed down and there is room to move
|
||||
} else if key == terminal.KeySpace {
|
||||
// the option they have selected
|
||||
if m.selectedIndex < len(options) {
|
||||
selectedOpt := options[m.selectedIndex]
|
||||
|
||||
// if we haven't seen this index before
|
||||
if old, ok := m.checked[selectedOpt.Index]; !ok {
|
||||
// set the value to true
|
||||
m.checked[selectedOpt.Index] = true
|
||||
} else {
|
||||
// otherwise just invert the current value
|
||||
m.checked[selectedOpt.Index] = !old
|
||||
}
|
||||
if !config.KeepFilter {
|
||||
m.filter = ""
|
||||
}
|
||||
}
|
||||
// only show the help message if we have one to show
|
||||
} else if string(key) == config.HelpInput && m.Help != "" {
|
||||
m.showingHelp = true
|
||||
} else if key == terminal.KeyEscape {
|
||||
m.VimMode = !m.VimMode
|
||||
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
|
||||
m.filter = ""
|
||||
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
|
||||
if m.filter != "" {
|
||||
runeFilter := []rune(m.filter)
|
||||
m.filter = string(runeFilter[0 : len(runeFilter)-1])
|
||||
}
|
||||
} else if key >= terminal.KeySpace {
|
||||
m.filter += string(key)
|
||||
m.VimMode = false
|
||||
} else if key == terminal.KeyArrowRight {
|
||||
for _, v := range options {
|
||||
m.checked[v.Index] = true
|
||||
}
|
||||
if !config.KeepFilter {
|
||||
m.filter = ""
|
||||
}
|
||||
} else if key == terminal.KeyArrowLeft {
|
||||
for _, v := range options {
|
||||
m.checked[v.Index] = false
|
||||
}
|
||||
if !config.KeepFilter {
|
||||
m.filter = ""
|
||||
}
|
||||
}
|
||||
|
||||
m.FilterMessage = ""
|
||||
if m.filter != "" {
|
||||
m.FilterMessage = " " + m.filter
|
||||
}
|
||||
if oldFilter != m.filter {
|
||||
// filter changed
|
||||
options = m.filterOptions(config)
|
||||
if len(options) > 0 && len(options) <= m.selectedIndex {
|
||||
m.selectedIndex = len(options) - 1
|
||||
}
|
||||
}
|
||||
// paginate the options
|
||||
// figure out the page size
|
||||
pageSize := m.PageSize
|
||||
// if we dont have a specific one
|
||||
if pageSize == 0 {
|
||||
// grab the global value
|
||||
pageSize = config.PageSize
|
||||
}
|
||||
|
||||
// TODO if we have started filtering and were looking at the end of a list
|
||||
// and we have modified the filter then we should move the page back!
|
||||
opts, idx := paginate(pageSize, options, m.selectedIndex)
|
||||
|
||||
tmplData := MultiSelectTemplateData{
|
||||
MultiSelect: *m,
|
||||
SelectedIndex: idx,
|
||||
Checked: m.checked,
|
||||
ShowHelp: m.showingHelp,
|
||||
PageEntries: opts,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
// render the options
|
||||
m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
|
||||
}
|
||||
|
||||
func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer {
|
||||
// the filtered list
|
||||
answers := []core.OptionAnswer{}
|
||||
|
||||
// if there is no filter applied
|
||||
if m.filter == "" {
|
||||
// return all of the options
|
||||
return core.OptionAnswerList(m.Options)
|
||||
}
|
||||
|
||||
// the filter to apply
|
||||
filter := m.Filter
|
||||
if filter == nil {
|
||||
filter = config.Filter
|
||||
}
|
||||
|
||||
// apply the filter to each option
|
||||
for i, opt := range m.Options {
|
||||
// i the filter says to include the option
|
||||
if filter(m.filter, opt, i) {
|
||||
answers = append(answers, core.OptionAnswer{
|
||||
Index: i,
|
||||
Value: opt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// we're done here
|
||||
return answers
|
||||
}
|
||||
|
||||
func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// compute the default state
|
||||
m.checked = make(map[int]bool)
|
||||
// if there is a default
|
||||
if m.Default != nil {
|
||||
// if the default is string values
|
||||
if defaultValues, ok := m.Default.([]string); ok {
|
||||
for _, dflt := range defaultValues {
|
||||
for i, opt := range m.Options {
|
||||
// if the option corresponds to the default
|
||||
if opt == dflt {
|
||||
// we found our initial value
|
||||
m.checked[i] = true
|
||||
// stop looking
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the default value is index values
|
||||
} else if defaultIndices, ok := m.Default.([]int); ok {
|
||||
// go over every index we need to enable by default
|
||||
for _, idx := range defaultIndices {
|
||||
// and enable it
|
||||
m.checked[idx] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no options to render
|
||||
if len(m.Options) == 0 {
|
||||
// we failed
|
||||
return "", errors.New("please provide options to select from")
|
||||
}
|
||||
|
||||
// figure out the page size
|
||||
pageSize := m.PageSize
|
||||
// if we dont have a specific one
|
||||
if pageSize == 0 {
|
||||
// grab the global value
|
||||
pageSize = config.PageSize
|
||||
}
|
||||
// paginate the options
|
||||
// build up a list of option answers
|
||||
opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex)
|
||||
|
||||
cursor := m.NewCursor()
|
||||
cursor.Save() // for proper cursor placement during selection
|
||||
cursor.Hide() // hide the cursor
|
||||
defer cursor.Show() // show the cursor when we're done
|
||||
defer cursor.Restore() // clear any accessibility offsetting on exit
|
||||
|
||||
tmplData := MultiSelectTemplateData{
|
||||
MultiSelect: *m,
|
||||
SelectedIndex: idx,
|
||||
Checked: m.checked,
|
||||
PageEntries: opts,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
// ask the question
|
||||
err := m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rr := m.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
// start waiting for input
|
||||
for {
|
||||
r, _, err := rr.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r == '\r' || r == '\n' {
|
||||
break
|
||||
}
|
||||
if r == terminal.KeyInterrupt {
|
||||
return "", terminal.InterruptErr
|
||||
}
|
||||
if r == terminal.KeyEndTransmission {
|
||||
break
|
||||
}
|
||||
m.OnChange(r, config)
|
||||
}
|
||||
m.filter = ""
|
||||
m.FilterMessage = ""
|
||||
|
||||
answers := []core.OptionAnswer{}
|
||||
for i, option := range m.Options {
|
||||
if val, ok := m.checked[i]; ok && val {
|
||||
answers = append(answers, core.OptionAnswer{Value: option, Index: i})
|
||||
}
|
||||
}
|
||||
|
||||
return answers, nil
|
||||
}
|
||||
|
||||
// Cleanup removes the options section, and renders the ask like a normal question.
|
||||
func (m *MultiSelect) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
// the answer to show
|
||||
answer := ""
|
||||
for _, ans := range val.([]core.OptionAnswer) {
|
||||
answer = fmt.Sprintf("%s, %s", answer, ans.Value)
|
||||
}
|
||||
|
||||
// if we answered anything
|
||||
if len(answer) > 2 {
|
||||
// remove the precending commas
|
||||
answer = answer[2:]
|
||||
}
|
||||
|
||||
// execute the output summary template with the answer
|
||||
return m.Render(
|
||||
MultiSelectQuestionTemplate,
|
||||
MultiSelectTemplateData{
|
||||
MultiSelect: *m,
|
||||
SelectedIndex: m.selectedIndex,
|
||||
Checked: m.checked,
|
||||
Answer: answer,
|
||||
ShowAnswer: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
}
|
101
vendor/github.com/AlecAivazis/survey/v2/password.go
generated
vendored
101
vendor/github.com/AlecAivazis/survey/v2/password.go
generated
vendored
|
@ -1,101 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
/*
|
||||
Password is like a normal Input but the text shows up as *'s and there is no default. Response
|
||||
type is a string.
|
||||
|
||||
password := ""
|
||||
prompt := &survey.Password{ Message: "Please type your password" }
|
||||
survey.AskOne(prompt, &password)
|
||||
*/
|
||||
type Password struct {
|
||||
Renderer
|
||||
Message string
|
||||
Help string
|
||||
}
|
||||
|
||||
type PasswordTemplateData struct {
|
||||
Password
|
||||
ShowHelp bool
|
||||
Config *PromptConfig
|
||||
}
|
||||
|
||||
// PasswordQuestionTemplate is a template with color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
|
||||
var PasswordQuestionTemplate = `
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
|
||||
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}`
|
||||
|
||||
func (p *Password) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// render the question template
|
||||
userOut, _, err := core.RunTemplate(
|
||||
PasswordQuestionTemplate,
|
||||
PasswordTemplateData{
|
||||
Password: *p,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
fmt.Fprint(terminal.NewAnsiStdout(p.Stdio().Out), userOut)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rr := p.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
// no help msg? Just return any response
|
||||
if p.Help == "" {
|
||||
line, err := rr.ReadLine('*')
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
cursor := p.NewCursor()
|
||||
|
||||
line := []rune{}
|
||||
// process answers looking for help prompt answer
|
||||
for {
|
||||
line, err = rr.ReadLine('*')
|
||||
if err != nil {
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
if string(line) == config.HelpInput {
|
||||
// terminal will echo the \n so we need to jump back up one row
|
||||
cursor.PreviousLine(1)
|
||||
|
||||
err = p.Render(
|
||||
PasswordQuestionTemplate,
|
||||
PasswordTemplateData{
|
||||
Password: *p,
|
||||
ShowHelp: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
lineStr := string(line)
|
||||
p.AppendRenderedText(strings.Repeat("*", len(lineStr)))
|
||||
return lineStr, err
|
||||
}
|
||||
|
||||
// Cleanup hides the string with a fixed number of characters.
|
||||
func (prompt *Password) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
return nil
|
||||
}
|
192
vendor/github.com/AlecAivazis/survey/v2/renderer.go
generated
vendored
192
vendor/github.com/AlecAivazis/survey/v2/renderer.go
generated
vendored
|
@ -1,192 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type Renderer struct {
|
||||
stdio terminal.Stdio
|
||||
renderedErrors bytes.Buffer
|
||||
renderedText bytes.Buffer
|
||||
}
|
||||
|
||||
type ErrorTemplateData struct {
|
||||
Error error
|
||||
Icon Icon
|
||||
}
|
||||
|
||||
var ErrorTemplate = `{{color .Icon.Format }}{{ .Icon.Text }} Sorry, your reply was invalid: {{ .Error.Error }}{{color "reset"}}
|
||||
`
|
||||
|
||||
func (r *Renderer) WithStdio(stdio terminal.Stdio) {
|
||||
r.stdio = stdio
|
||||
}
|
||||
|
||||
func (r *Renderer) Stdio() terminal.Stdio {
|
||||
return r.stdio
|
||||
}
|
||||
|
||||
func (r *Renderer) NewRuneReader() *terminal.RuneReader {
|
||||
return terminal.NewRuneReader(r.stdio)
|
||||
}
|
||||
|
||||
func (r *Renderer) NewCursor() *terminal.Cursor {
|
||||
return &terminal.Cursor{
|
||||
In: r.stdio.In,
|
||||
Out: r.stdio.Out,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Renderer) Error(config *PromptConfig, invalid error) error {
|
||||
// cleanup the currently rendered errors
|
||||
r.resetPrompt(r.countLines(r.renderedErrors))
|
||||
r.renderedErrors.Reset()
|
||||
|
||||
// cleanup the rest of the prompt
|
||||
r.resetPrompt(r.countLines(r.renderedText))
|
||||
r.renderedText.Reset()
|
||||
|
||||
userOut, layoutOut, err := core.RunTemplate(ErrorTemplate, &ErrorTemplateData{
|
||||
Error: invalid,
|
||||
Icon: config.Icons.Error,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send the message to the user
|
||||
fmt.Fprint(terminal.NewAnsiStdout(r.stdio.Out), userOut)
|
||||
|
||||
// add the printed text to the rendered error buffer so we can cleanup later
|
||||
r.appendRenderedError(layoutOut)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Renderer) OffsetCursor(offset int) {
|
||||
cursor := r.NewCursor()
|
||||
for offset > 0 {
|
||||
cursor.PreviousLine(1)
|
||||
offset--
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Renderer) Render(tmpl string, data interface{}) error {
|
||||
// cleanup the currently rendered text
|
||||
lineCount := r.countLines(r.renderedText)
|
||||
r.resetPrompt(lineCount)
|
||||
r.renderedText.Reset()
|
||||
|
||||
// render the template summarizing the current state
|
||||
userOut, layoutOut, err := core.RunTemplate(tmpl, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// print the summary
|
||||
fmt.Fprint(terminal.NewAnsiStdout(r.stdio.Out), userOut)
|
||||
|
||||
// add the printed text to the rendered text buffer so we can cleanup later
|
||||
r.AppendRenderedText(layoutOut)
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderWithCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx int) error {
|
||||
cursor := r.NewCursor()
|
||||
cursor.Restore() // clear any accessibility offsetting
|
||||
|
||||
if err := r.Render(tmpl, data); err != nil {
|
||||
return err
|
||||
}
|
||||
cursor.Save()
|
||||
|
||||
offset := computeCursorOffset(MultiSelectQuestionTemplate, data, opts, idx, r.termWidthSafe())
|
||||
r.OffsetCursor(offset)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// appendRenderedError appends text to the renderer's error buffer
|
||||
// which is used to track what has been printed. It is not exported
|
||||
// as errors should only be displayed via Error(config, error).
|
||||
func (r *Renderer) appendRenderedError(text string) {
|
||||
r.renderedErrors.WriteString(text)
|
||||
}
|
||||
|
||||
// AppendRenderedText appends text to the renderer's text buffer
|
||||
// which is used to track of what has been printed. The buffer is used
|
||||
// to calculate how many lines to erase before updating the prompt.
|
||||
func (r *Renderer) AppendRenderedText(text string) {
|
||||
r.renderedText.WriteString(text)
|
||||
}
|
||||
|
||||
func (r *Renderer) resetPrompt(lines int) {
|
||||
// clean out current line in case tmpl didnt end in newline
|
||||
cursor := r.NewCursor()
|
||||
cursor.HorizontalAbsolute(0)
|
||||
terminal.EraseLine(r.stdio.Out, terminal.ERASE_LINE_ALL)
|
||||
// clean up what we left behind last time
|
||||
for i := 0; i < lines; i++ {
|
||||
cursor.PreviousLine(1)
|
||||
terminal.EraseLine(r.stdio.Out, terminal.ERASE_LINE_ALL)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Renderer) termWidth() (int, error) {
|
||||
fd := int(r.stdio.Out.Fd())
|
||||
termWidth, _, err := term.GetSize(fd)
|
||||
return termWidth, err
|
||||
}
|
||||
|
||||
func (r *Renderer) termWidthSafe() int {
|
||||
w, err := r.termWidth()
|
||||
if err != nil || w == 0 {
|
||||
// if we got an error due to terminal.GetSize not being supported
|
||||
// on current platform then just assume a very wide terminal
|
||||
w = 10000
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// countLines will return the count of `\n` with the addition of any
|
||||
// lines that have wrapped due to narrow terminal width
|
||||
func (r *Renderer) countLines(buf bytes.Buffer) int {
|
||||
w := r.termWidthSafe()
|
||||
|
||||
bufBytes := buf.Bytes()
|
||||
|
||||
count := 0
|
||||
curr := 0
|
||||
delim := -1
|
||||
for curr < len(bufBytes) {
|
||||
// read until the next newline or the end of the string
|
||||
relDelim := bytes.IndexRune(bufBytes[curr:], '\n')
|
||||
if relDelim != -1 {
|
||||
count += 1 // new line found, add it to the count
|
||||
delim = curr + relDelim
|
||||
} else {
|
||||
delim = len(bufBytes) // no new line found, read rest of text
|
||||
}
|
||||
|
||||
if lineWidth := utf8.RuneCount(bufBytes[curr:delim]); lineWidth > w {
|
||||
// account for word wrapping
|
||||
count += lineWidth / w
|
||||
if (lineWidth % w) == 0 {
|
||||
// content whose width is exactly a multiplier of available width should not
|
||||
// count as having wrapped on the last line
|
||||
count -= 1
|
||||
}
|
||||
}
|
||||
curr = delim + 1
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
347
vendor/github.com/AlecAivazis/survey/v2/select.go
generated
vendored
347
vendor/github.com/AlecAivazis/survey/v2/select.go
generated
vendored
|
@ -1,347 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
/*
|
||||
Select is a prompt that presents a list of various options to the user
|
||||
for them to select using the arrow keys and enter. Response type is a string.
|
||||
|
||||
color := ""
|
||||
prompt := &survey.Select{
|
||||
Message: "Choose a color:",
|
||||
Options: []string{"red", "blue", "green"},
|
||||
}
|
||||
survey.AskOne(prompt, &color)
|
||||
*/
|
||||
type Select struct {
|
||||
Renderer
|
||||
Message string
|
||||
Options []string
|
||||
Default interface{}
|
||||
Help string
|
||||
PageSize int
|
||||
VimMode bool
|
||||
FilterMessage string
|
||||
Filter func(filter string, value string, index int) bool
|
||||
filter string
|
||||
selectedIndex int
|
||||
useDefault bool
|
||||
showingHelp bool
|
||||
}
|
||||
|
||||
// SelectTemplateData is the data available to the templates when processing
|
||||
type SelectTemplateData struct {
|
||||
Select
|
||||
PageEntries []core.OptionAnswer
|
||||
SelectedIndex int
|
||||
Answer string
|
||||
ShowAnswer bool
|
||||
ShowHelp bool
|
||||
Config *PromptConfig
|
||||
|
||||
// These fields are used when rendering an individual option
|
||||
CurrentOpt core.OptionAnswer
|
||||
CurrentIndex int
|
||||
}
|
||||
|
||||
// IterateOption sets CurrentOpt and CurrentIndex appropriately so a select option can be rendered individually
|
||||
func (s SelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} {
|
||||
copy := s
|
||||
copy.CurrentIndex = ix
|
||||
copy.CurrentOpt = opt
|
||||
return copy
|
||||
}
|
||||
|
||||
var SelectQuestionTemplate = `
|
||||
{{- define "option"}}
|
||||
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
|
||||
{{- .CurrentOpt.Value}}
|
||||
{{- color "reset"}}
|
||||
{{end}}
|
||||
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
|
||||
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
|
||||
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
|
||||
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
|
||||
{{- else}}
|
||||
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
|
||||
{{- "\n"}}
|
||||
{{- range $ix, $option := .PageEntries}}
|
||||
{{- template "option" $.IterateOption $ix $option}}
|
||||
{{- end}}
|
||||
{{- end}}`
|
||||
|
||||
// OnChange is called on every keypress.
|
||||
func (s *Select) OnChange(key rune, config *PromptConfig) bool {
|
||||
options := s.filterOptions(config)
|
||||
oldFilter := s.filter
|
||||
|
||||
// if the user pressed the enter key and the index is a valid option
|
||||
if key == terminal.KeyEnter || key == '\n' {
|
||||
// if the selected index is a valid option
|
||||
if len(options) > 0 && s.selectedIndex < len(options) {
|
||||
|
||||
// we're done (stop prompting the user)
|
||||
return true
|
||||
}
|
||||
|
||||
// we're not done (keep prompting)
|
||||
return false
|
||||
|
||||
// if the user pressed the up arrow or 'k' to emulate vim
|
||||
} else if (key == terminal.KeyArrowUp || (s.VimMode && key == 'k')) && len(options) > 0 {
|
||||
s.useDefault = false
|
||||
|
||||
// if we are at the top of the list
|
||||
if s.selectedIndex == 0 {
|
||||
// start from the button
|
||||
s.selectedIndex = len(options) - 1
|
||||
} else {
|
||||
// otherwise we are not at the top of the list so decrement the selected index
|
||||
s.selectedIndex--
|
||||
}
|
||||
|
||||
// if the user pressed down or 'j' to emulate vim
|
||||
} else if (key == terminal.KeyTab || key == terminal.KeyArrowDown || (s.VimMode && key == 'j')) && len(options) > 0 {
|
||||
s.useDefault = false
|
||||
// if we are at the bottom of the list
|
||||
if s.selectedIndex == len(options)-1 {
|
||||
// start from the top
|
||||
s.selectedIndex = 0
|
||||
} else {
|
||||
// increment the selected index
|
||||
s.selectedIndex++
|
||||
}
|
||||
// only show the help message if we have one
|
||||
} else if string(key) == config.HelpInput && s.Help != "" {
|
||||
s.showingHelp = true
|
||||
// if the user wants to toggle vim mode on/off
|
||||
} else if key == terminal.KeyEscape {
|
||||
s.VimMode = !s.VimMode
|
||||
// if the user hits any of the keys that clear the filter
|
||||
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
|
||||
s.filter = ""
|
||||
// if the user is deleting a character in the filter
|
||||
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
|
||||
// if there is content in the filter to delete
|
||||
if s.filter != "" {
|
||||
runeFilter := []rune(s.filter)
|
||||
// subtract a line from the current filter
|
||||
s.filter = string(runeFilter[0 : len(runeFilter)-1])
|
||||
// we removed the last value in the filter
|
||||
}
|
||||
} else if key >= terminal.KeySpace {
|
||||
s.filter += string(key)
|
||||
// make sure vim mode is disabled
|
||||
s.VimMode = false
|
||||
// make sure that we use the current value in the filtered list
|
||||
s.useDefault = false
|
||||
}
|
||||
|
||||
s.FilterMessage = ""
|
||||
if s.filter != "" {
|
||||
s.FilterMessage = " " + s.filter
|
||||
}
|
||||
if oldFilter != s.filter {
|
||||
// filter changed
|
||||
options = s.filterOptions(config)
|
||||
if len(options) > 0 && len(options) <= s.selectedIndex {
|
||||
s.selectedIndex = len(options) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// figure out the options and index to render
|
||||
// figure out the page size
|
||||
pageSize := s.PageSize
|
||||
// if we dont have a specific one
|
||||
if pageSize == 0 {
|
||||
// grab the global value
|
||||
pageSize = config.PageSize
|
||||
}
|
||||
|
||||
// TODO if we have started filtering and were looking at the end of a list
|
||||
// and we have modified the filter then we should move the page back!
|
||||
opts, idx := paginate(pageSize, options, s.selectedIndex)
|
||||
|
||||
tmplData := SelectTemplateData{
|
||||
Select: *s,
|
||||
SelectedIndex: idx,
|
||||
ShowHelp: s.showingHelp,
|
||||
PageEntries: opts,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
// render the options
|
||||
s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
|
||||
|
||||
// keep prompting
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Select) filterOptions(config *PromptConfig) []core.OptionAnswer {
|
||||
// the filtered list
|
||||
answers := []core.OptionAnswer{}
|
||||
|
||||
// if there is no filter applied
|
||||
if s.filter == "" {
|
||||
return core.OptionAnswerList(s.Options)
|
||||
}
|
||||
|
||||
// the filter to apply
|
||||
filter := s.Filter
|
||||
if filter == nil {
|
||||
filter = config.Filter
|
||||
}
|
||||
|
||||
//
|
||||
for i, opt := range s.Options {
|
||||
// i the filter says to include the option
|
||||
if filter(s.filter, opt, i) {
|
||||
answers = append(answers, core.OptionAnswer{
|
||||
Index: i,
|
||||
Value: opt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// return the list of answers
|
||||
return answers
|
||||
}
|
||||
|
||||
func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
|
||||
// if there are no options to render
|
||||
if len(s.Options) == 0 {
|
||||
// we failed
|
||||
return "", errors.New("please provide options to select from")
|
||||
}
|
||||
|
||||
// start off with the first option selected
|
||||
sel := 0
|
||||
// if there is a default
|
||||
if s.Default != "" {
|
||||
// find the choice
|
||||
for i, opt := range s.Options {
|
||||
// if the option corresponds to the default
|
||||
if opt == s.Default {
|
||||
// we found our initial value
|
||||
sel = i
|
||||
// stop looking
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// save the selected index
|
||||
s.selectedIndex = sel
|
||||
|
||||
// figure out the page size
|
||||
pageSize := s.PageSize
|
||||
// if we dont have a specific one
|
||||
if pageSize == 0 {
|
||||
// grab the global value
|
||||
pageSize = config.PageSize
|
||||
}
|
||||
|
||||
// figure out the options and index to render
|
||||
opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel)
|
||||
|
||||
cursor := s.NewCursor()
|
||||
cursor.Save() // for proper cursor placement during selection
|
||||
cursor.Hide() // hide the cursor
|
||||
defer cursor.Show() // show the cursor when we're done
|
||||
defer cursor.Restore() // clear any accessibility offsetting on exit
|
||||
|
||||
tmplData := SelectTemplateData{
|
||||
Select: *s,
|
||||
SelectedIndex: idx,
|
||||
ShowHelp: s.showingHelp,
|
||||
PageEntries: opts,
|
||||
Config: config,
|
||||
}
|
||||
|
||||
// ask the question
|
||||
err := s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// by default, use the default value
|
||||
s.useDefault = true
|
||||
|
||||
rr := s.NewRuneReader()
|
||||
rr.SetTermMode()
|
||||
defer rr.RestoreTermMode()
|
||||
|
||||
// start waiting for input
|
||||
for {
|
||||
r, _, err := rr.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r == terminal.KeyInterrupt {
|
||||
return "", terminal.InterruptErr
|
||||
}
|
||||
if r == terminal.KeyEndTransmission {
|
||||
break
|
||||
}
|
||||
if s.OnChange(r, config) {
|
||||
break
|
||||
}
|
||||
}
|
||||
options := s.filterOptions(config)
|
||||
s.filter = ""
|
||||
s.FilterMessage = ""
|
||||
|
||||
// the index to report
|
||||
var val string
|
||||
// if we are supposed to use the default value
|
||||
if s.useDefault || s.selectedIndex >= len(options) {
|
||||
// if there is a default value
|
||||
if s.Default != nil {
|
||||
// if the default is a string
|
||||
if defaultString, ok := s.Default.(string); ok {
|
||||
// use the default value
|
||||
val = defaultString
|
||||
// the default value could also be an interpret which is interpretted as the index
|
||||
} else if defaultIndex, ok := s.Default.(int); ok {
|
||||
val = s.Options[defaultIndex]
|
||||
} else {
|
||||
return val, errors.New("default value of select must be an int or string")
|
||||
}
|
||||
} else if len(options) > 0 {
|
||||
// there is no default value so use the first
|
||||
val = options[0].Value
|
||||
}
|
||||
// otherwise the selected index points to the value
|
||||
} else if s.selectedIndex < len(options) {
|
||||
// the
|
||||
val = options[s.selectedIndex].Value
|
||||
}
|
||||
|
||||
// now that we have the value lets go hunt down the right index to return
|
||||
idx = -1
|
||||
for i, optionValue := range s.Options {
|
||||
if optionValue == val {
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
return core.OptionAnswer{Value: val, Index: idx}, err
|
||||
}
|
||||
|
||||
func (s *Select) Cleanup(config *PromptConfig, val interface{}) error {
|
||||
cursor := s.NewCursor()
|
||||
cursor.Restore()
|
||||
return s.Render(
|
||||
SelectQuestionTemplate,
|
||||
SelectTemplateData{
|
||||
Select: *s,
|
||||
Answer: val.(core.OptionAnswer).Value,
|
||||
ShowAnswer: true,
|
||||
Config: config,
|
||||
},
|
||||
)
|
||||
}
|
454
vendor/github.com/AlecAivazis/survey/v2/survey.go
generated
vendored
454
vendor/github.com/AlecAivazis/survey/v2/survey.go
generated
vendored
|
@ -1,454 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
)
|
||||
|
||||
// DefaultAskOptions is the default options on ask, using the OS stdio.
|
||||
func defaultAskOptions() *AskOptions {
|
||||
return &AskOptions{
|
||||
Stdio: terminal.Stdio{
|
||||
In: os.Stdin,
|
||||
Out: os.Stdout,
|
||||
Err: os.Stderr,
|
||||
},
|
||||
PromptConfig: PromptConfig{
|
||||
PageSize: 7,
|
||||
HelpInput: "?",
|
||||
SuggestInput: "tab",
|
||||
Icons: IconSet{
|
||||
Error: Icon{
|
||||
Text: "X",
|
||||
Format: "red",
|
||||
},
|
||||
Help: Icon{
|
||||
Text: "?",
|
||||
Format: "cyan",
|
||||
},
|
||||
Question: Icon{
|
||||
Text: "?",
|
||||
Format: "green+hb",
|
||||
},
|
||||
MarkedOption: Icon{
|
||||
Text: "[x]",
|
||||
Format: "green",
|
||||
},
|
||||
UnmarkedOption: Icon{
|
||||
Text: "[ ]",
|
||||
Format: "default+hb",
|
||||
},
|
||||
SelectFocus: Icon{
|
||||
Text: ">",
|
||||
Format: "cyan+b",
|
||||
},
|
||||
},
|
||||
Filter: func(filter string, value string, index int) (include bool) {
|
||||
filter = strings.ToLower(filter)
|
||||
|
||||
// include this option if it matches
|
||||
return strings.Contains(strings.ToLower(value), filter)
|
||||
},
|
||||
KeepFilter: false,
|
||||
ShowCursor: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
func defaultPromptConfig() *PromptConfig {
|
||||
return &defaultAskOptions().PromptConfig
|
||||
}
|
||||
|
||||
func defaultIcons() *IconSet {
|
||||
return &defaultPromptConfig().Icons
|
||||
}
|
||||
|
||||
// OptionAnswer is an ergonomic alias for core.OptionAnswer
|
||||
type OptionAnswer = core.OptionAnswer
|
||||
|
||||
// Icon holds the text and format to show for a particular icon
|
||||
type Icon struct {
|
||||
Text string
|
||||
Format string
|
||||
}
|
||||
|
||||
// IconSet holds the icons to use for various prompts
|
||||
type IconSet struct {
|
||||
HelpInput Icon
|
||||
Error Icon
|
||||
Help Icon
|
||||
Question Icon
|
||||
MarkedOption Icon
|
||||
UnmarkedOption Icon
|
||||
SelectFocus Icon
|
||||
}
|
||||
|
||||
// Validator is a function passed to a Question after a user has provided a response.
|
||||
// If the function returns an error, then the user will be prompted again for another
|
||||
// response.
|
||||
type Validator func(ans interface{}) error
|
||||
|
||||
// Transformer is a function passed to a Question after a user has provided a response.
|
||||
// The function can be used to implement a custom logic that will result to return
|
||||
// a different representation of the given answer.
|
||||
//
|
||||
// Look `TransformString`, `ToLower` `Title` and `ComposeTransformers` for more.
|
||||
type Transformer func(ans interface{}) (newAns interface{})
|
||||
|
||||
// Question is the core data structure for a survey questionnaire.
|
||||
type Question struct {
|
||||
Name string
|
||||
Prompt Prompt
|
||||
Validate Validator
|
||||
Transform Transformer
|
||||
}
|
||||
|
||||
// PromptConfig holds the global configuration for a prompt
|
||||
type PromptConfig struct {
|
||||
PageSize int
|
||||
Icons IconSet
|
||||
HelpInput string
|
||||
SuggestInput string
|
||||
Filter func(filter string, option string, index int) bool
|
||||
KeepFilter bool
|
||||
ShowCursor bool
|
||||
}
|
||||
|
||||
// Prompt is the primary interface for the objects that can take user input
|
||||
// and return a response.
|
||||
type Prompt interface {
|
||||
Prompt(config *PromptConfig) (interface{}, error)
|
||||
Cleanup(*PromptConfig, interface{}) error
|
||||
Error(*PromptConfig, error) error
|
||||
}
|
||||
|
||||
// PromptAgainer Interface for Prompts that support prompting again after invalid input
|
||||
type PromptAgainer interface {
|
||||
PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error)
|
||||
}
|
||||
|
||||
// AskOpt allows setting optional ask options.
|
||||
type AskOpt func(options *AskOptions) error
|
||||
|
||||
// AskOptions provides additional options on ask.
|
||||
type AskOptions struct {
|
||||
Stdio terminal.Stdio
|
||||
Validators []Validator
|
||||
PromptConfig PromptConfig
|
||||
}
|
||||
|
||||
// WithStdio specifies the standard input, output and error files survey
|
||||
// interacts with. By default, these are os.Stdin, os.Stdout, and os.Stderr.
|
||||
func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
options.Stdio.In = in
|
||||
options.Stdio.Out = out
|
||||
options.Stdio.Err = err
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithFilter specifies the default filter to use when asking questions.
|
||||
func WithFilter(filter func(filter string, value string, index int) (include bool)) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// save the filter internally
|
||||
options.PromptConfig.Filter = filter
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeepFilter sets the if the filter is kept after selections
|
||||
func WithKeepFilter(KeepFilter bool) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// set the page size
|
||||
options.PromptConfig.KeepFilter = KeepFilter
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithValidator specifies a validator to use while prompting the user
|
||||
func WithValidator(v Validator) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// add the provided validator to the list
|
||||
options.Validators = append(options.Validators, v)
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type wantsStdio interface {
|
||||
WithStdio(terminal.Stdio)
|
||||
}
|
||||
|
||||
// WithPageSize sets the default page size used by prompts
|
||||
func WithPageSize(pageSize int) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// set the page size
|
||||
options.PromptConfig.PageSize = pageSize
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithHelpInput changes the character that prompts look for to give the user helpful information.
|
||||
func WithHelpInput(r rune) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// set the input character
|
||||
options.PromptConfig.HelpInput = string(r)
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithIcons sets the icons that will be used when prompting the user
|
||||
func WithIcons(setIcons func(*IconSet)) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// update the default icons with whatever the user says
|
||||
setIcons(&options.PromptConfig.Icons)
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithShowCursor sets the show cursor behavior when prompting the user
|
||||
func WithShowCursor(ShowCursor bool) AskOpt {
|
||||
return func(options *AskOptions) error {
|
||||
// set the page size
|
||||
options.PromptConfig.ShowCursor = ShowCursor
|
||||
|
||||
// nothing went wrong
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
AskOne performs the prompt for a single prompt and asks for validation if required.
|
||||
Response types should be something that can be casted from the response type designated
|
||||
in the documentation. For example:
|
||||
|
||||
name := ""
|
||||
prompt := &survey.Input{
|
||||
Message: "name",
|
||||
}
|
||||
|
||||
survey.AskOne(prompt, &name)
|
||||
|
||||
*/
|
||||
func AskOne(p Prompt, response interface{}, opts ...AskOpt) error {
|
||||
err := Ask([]*Question{{Prompt: p}}, response, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Ask performs the prompt loop, asking for validation when appropriate. The response
|
||||
type can be one of two options. If a struct is passed, the answer will be written to
|
||||
the field whose name matches the Name field on the corresponding question. Field types
|
||||
should be something that can be casted from the response type designated in the
|
||||
documentation. Note, a survey tag can also be used to identify a Otherwise, a
|
||||
map[string]interface{} can be passed, responses will be written to the key with the
|
||||
matching name. For example:
|
||||
|
||||
qs := []*survey.Question{
|
||||
{
|
||||
Name: "name",
|
||||
Prompt: &survey.Input{Message: "What is your name?"},
|
||||
Validate: survey.Required,
|
||||
Transform: survey.Title,
|
||||
},
|
||||
}
|
||||
|
||||
answers := struct{ Name string }{}
|
||||
|
||||
|
||||
err := survey.Ask(qs, &answers)
|
||||
*/
|
||||
func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
|
||||
// build up the configuration options
|
||||
options := defaultAskOptions()
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
continue
|
||||
}
|
||||
if err := opt(options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if we weren't passed a place to record the answers
|
||||
if response == nil {
|
||||
// we can't go any further
|
||||
return errors.New("cannot call Ask() with a nil reference to record the answers")
|
||||
}
|
||||
|
||||
// go over every question
|
||||
for _, q := range qs {
|
||||
// If Prompt implements controllable stdio, pass in specified stdio.
|
||||
if p, ok := q.Prompt.(wantsStdio); ok {
|
||||
p.WithStdio(options.Stdio)
|
||||
}
|
||||
|
||||
// grab the user input and save it
|
||||
ans, err := q.Prompt.Prompt(&options.PromptConfig)
|
||||
// if there was a problem
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build up a list of validators that we have to apply to this question
|
||||
validators := []Validator{}
|
||||
|
||||
// make sure to include the question specific one
|
||||
if q.Validate != nil {
|
||||
validators = append(validators, q.Validate)
|
||||
}
|
||||
// add any "global" validators
|
||||
for _, validator := range options.Validators {
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
|
||||
// apply every validator to thte response
|
||||
for _, validator := range validators {
|
||||
// wait for a valid response
|
||||
for invalid := validator(ans); invalid != nil; invalid = validator(ans) {
|
||||
err := q.Prompt.Error(&options.PromptConfig, invalid)
|
||||
// if there was a problem
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ask for more input
|
||||
if promptAgainer, ok := q.Prompt.(PromptAgainer); ok {
|
||||
ans, err = promptAgainer.PromptAgain(&options.PromptConfig, ans, invalid)
|
||||
} else {
|
||||
ans, err = q.Prompt.Prompt(&options.PromptConfig)
|
||||
}
|
||||
// if there was a problem
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if q.Transform != nil {
|
||||
// check if we have a transformer available, if so
|
||||
// then try to acquire the new representation of the
|
||||
// answer, if the resulting answer is not nil.
|
||||
if newAns := q.Transform(ans); newAns != nil {
|
||||
ans = newAns
|
||||
}
|
||||
}
|
||||
|
||||
// tell the prompt to cleanup with the validated value
|
||||
q.Prompt.Cleanup(&options.PromptConfig, ans)
|
||||
|
||||
// if something went wrong
|
||||
if err != nil {
|
||||
// stop listening
|
||||
return err
|
||||
}
|
||||
|
||||
// add it to the map
|
||||
err = core.WriteAnswer(response, q.Name, ans)
|
||||
// if something went wrong
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// return the response
|
||||
return nil
|
||||
}
|
||||
|
||||
// paginate returns a single page of choices given the page size, the total list of
|
||||
// possible choices, and the current selected index in the total list.
|
||||
func paginate(pageSize int, choices []core.OptionAnswer, sel int) ([]core.OptionAnswer, int) {
|
||||
var start, end, cursor int
|
||||
|
||||
if len(choices) < pageSize {
|
||||
// if we dont have enough options to fill a page
|
||||
start = 0
|
||||
end = len(choices)
|
||||
cursor = sel
|
||||
|
||||
} else if sel < pageSize/2 {
|
||||
// if we are in the first half page
|
||||
start = 0
|
||||
end = pageSize
|
||||
cursor = sel
|
||||
|
||||
} else if len(choices)-sel-1 < pageSize/2 {
|
||||
// if we are in the last half page
|
||||
start = len(choices) - pageSize
|
||||
end = len(choices)
|
||||
cursor = sel - start
|
||||
|
||||
} else {
|
||||
// somewhere in the middle
|
||||
above := pageSize / 2
|
||||
below := pageSize - above
|
||||
|
||||
cursor = pageSize / 2
|
||||
start = sel - above
|
||||
end = sel + below
|
||||
}
|
||||
|
||||
// return the subset we care about and the index
|
||||
return choices[start:end], cursor
|
||||
}
|
||||
|
||||
type IterableOpts interface {
|
||||
IterateOption(int, core.OptionAnswer) interface{}
|
||||
}
|
||||
|
||||
func computeCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx, tWidth int) int {
|
||||
tmpls, err := core.GetTemplatePair(tmpl)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
t := tmpls[0]
|
||||
|
||||
renderOpt := func(ix int, opt core.OptionAnswer) string {
|
||||
buf := bytes.NewBufferString("")
|
||||
t.ExecuteTemplate(buf, "option", data.IterateOption(ix, opt))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
offset := len(opts) - idx
|
||||
|
||||
for i, o := range opts {
|
||||
if i < idx {
|
||||
continue
|
||||
}
|
||||
renderedOpt := renderOpt(i, o)
|
||||
valWidth := utf8.RuneCount([]byte(renderedOpt))
|
||||
if valWidth > tWidth {
|
||||
splitCount := valWidth / tWidth
|
||||
if valWidth%tWidth == 0 {
|
||||
splitCount -= 1
|
||||
}
|
||||
offset += splitCount
|
||||
}
|
||||
}
|
||||
|
||||
return offset
|
||||
}
|
22
vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt
generated
vendored
22
vendor/github.com/AlecAivazis/survey/v2/terminal/LICENSE.txt
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2014 Takashi Kokubun
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
3
vendor/github.com/AlecAivazis/survey/v2/terminal/README.md
generated
vendored
3
vendor/github.com/AlecAivazis/survey/v2/terminal/README.md
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
# survey/terminal
|
||||
|
||||
This package started as a copy of [kokuban/go-ansi](http://github.com/k0kubun/go-ansi) but has since been modified to fit survey's specific needs.
|
22
vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go
generated
vendored
22
vendor/github.com/AlecAivazis/survey/v2/terminal/buffered_reader.go
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type BufferedReader struct {
|
||||
In io.Reader
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (br *BufferedReader) Read(p []byte) (int, error) {
|
||||
n, err := br.Buffer.Read(p)
|
||||
if err != nil && err != io.EOF {
|
||||
return n, err
|
||||
} else if err == nil {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return br.In.Read(p[n:])
|
||||
}
|
190
vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go
generated
vendored
190
vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go
generated
vendored
|
@ -1,190 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var COORDINATE_SYSTEM_BEGIN Short = 1
|
||||
|
||||
var dsrPattern = regexp.MustCompile(`\x1b\[(\d+);(\d+)R$`)
|
||||
|
||||
type Cursor struct {
|
||||
In FileReader
|
||||
Out FileWriter
|
||||
}
|
||||
|
||||
// Up moves the cursor n cells to up.
|
||||
func (c *Cursor) Up(n int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%dA", n)
|
||||
}
|
||||
|
||||
// Down moves the cursor n cells to down.
|
||||
func (c *Cursor) Down(n int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%dB", n)
|
||||
}
|
||||
|
||||
// Forward moves the cursor n cells to right.
|
||||
func (c *Cursor) Forward(n int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%dC", n)
|
||||
}
|
||||
|
||||
// Back moves the cursor n cells to left.
|
||||
func (c *Cursor) Back(n int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%dD", n)
|
||||
}
|
||||
|
||||
// NextLine moves cursor to beginning of the line n lines down.
|
||||
func (c *Cursor) NextLine(n int) {
|
||||
c.Down(1)
|
||||
c.HorizontalAbsolute(0)
|
||||
}
|
||||
|
||||
// PreviousLine moves cursor to beginning of the line n lines up.
|
||||
func (c *Cursor) PreviousLine(n int) {
|
||||
c.Up(1)
|
||||
c.HorizontalAbsolute(0)
|
||||
}
|
||||
|
||||
// HorizontalAbsolute moves cursor horizontally to x.
|
||||
func (c *Cursor) HorizontalAbsolute(x int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%dG", x)
|
||||
}
|
||||
|
||||
// Show shows the cursor.
|
||||
func (c *Cursor) Show() {
|
||||
fmt.Fprint(c.Out, "\x1b[?25h")
|
||||
}
|
||||
|
||||
// Hide hide the cursor.
|
||||
func (c *Cursor) Hide() {
|
||||
fmt.Fprint(c.Out, "\x1b[?25l")
|
||||
}
|
||||
|
||||
// Move moves the cursor to a specific x,y location.
|
||||
func (c *Cursor) Move(x int, y int) {
|
||||
fmt.Fprintf(c.Out, "\x1b[%d;%df", x, y)
|
||||
}
|
||||
|
||||
// Save saves the current position
|
||||
func (c *Cursor) Save() {
|
||||
fmt.Fprint(c.Out, "\x1b7")
|
||||
}
|
||||
|
||||
// Restore restores the saved position of the cursor
|
||||
func (c *Cursor) Restore() {
|
||||
fmt.Fprint(c.Out, "\x1b8")
|
||||
}
|
||||
|
||||
// for comparability purposes between windows
|
||||
// in unix we need to print out a new line on some terminals
|
||||
func (c *Cursor) MoveNextLine(cur *Coord, terminalSize *Coord) {
|
||||
if cur.Y == terminalSize.Y {
|
||||
fmt.Fprintln(c.Out)
|
||||
}
|
||||
c.NextLine(1)
|
||||
}
|
||||
|
||||
// Location returns the current location of the cursor in the terminal
|
||||
func (c *Cursor) Location(buf *bytes.Buffer) (*Coord, error) {
|
||||
// ANSI escape sequence for DSR - Device Status Report
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
|
||||
fmt.Fprint(c.Out, "\x1b[6n")
|
||||
|
||||
// There may be input in Stdin prior to CursorLocation so make sure we don't
|
||||
// drop those bytes.
|
||||
var loc []int
|
||||
var match string
|
||||
for loc == nil {
|
||||
// Reports the cursor position (CPR) to the application as (as though typed at
|
||||
// the keyboard) ESC[n;mR, where n is the row and m is the column.
|
||||
reader := bufio.NewReader(c.In)
|
||||
text, err := reader.ReadSlice(byte('R'))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loc = dsrPattern.FindStringIndex(string(text))
|
||||
if loc == nil {
|
||||
// After reading slice to byte 'R', the bufio Reader may have read more
|
||||
// bytes into its internal buffer which will be discarded on next ReadSlice.
|
||||
// We create a temporary buffer to read the remaining buffered slice and
|
||||
// write them to output buffer.
|
||||
buffered := make([]byte, reader.Buffered())
|
||||
_, err = io.ReadFull(reader, buffered)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stdin contains R that doesn't match DSR, so pass the bytes along to
|
||||
// output buffer.
|
||||
buf.Write(text)
|
||||
buf.Write(buffered)
|
||||
} else {
|
||||
// Write the non-matching leading bytes to output buffer.
|
||||
buf.Write(text[:loc[0]])
|
||||
|
||||
// Save the matching bytes to extract the row and column of the cursor.
|
||||
match = string(text[loc[0]:loc[1]])
|
||||
}
|
||||
}
|
||||
|
||||
matches := dsrPattern.FindStringSubmatch(string(match))
|
||||
if len(matches) != 3 {
|
||||
return nil, fmt.Errorf("incorrect number of matches: %d", len(matches))
|
||||
}
|
||||
|
||||
col, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
row, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Coord{Short(col), Short(row)}, nil
|
||||
}
|
||||
|
||||
func (cur Coord) CursorIsAtLineEnd(size *Coord) bool {
|
||||
return cur.X == size.X
|
||||
}
|
||||
|
||||
func (cur Coord) CursorIsAtLineBegin() bool {
|
||||
return cur.X == COORDINATE_SYSTEM_BEGIN
|
||||
}
|
||||
|
||||
// Size returns the height and width of the terminal.
|
||||
func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) {
|
||||
// the general approach here is to move the cursor to the very bottom
|
||||
// of the terminal, ask for the current location and then move the
|
||||
// cursor back where we started
|
||||
|
||||
// hide the cursor (so it doesn't blink when getting the size of the terminal)
|
||||
c.Hide()
|
||||
defer c.Show()
|
||||
|
||||
// save the current location of the cursor
|
||||
c.Save()
|
||||
defer c.Restore()
|
||||
|
||||
// move the cursor to the very bottom of the terminal
|
||||
c.Move(999, 999)
|
||||
|
||||
// ask for the current location
|
||||
bottom, err := c.Location(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// since the bottom was calculated in the lower right corner, it
|
||||
// is the dimensions we are looking for
|
||||
return bottom, nil
|
||||
}
|
138
vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go
generated
vendored
138
vendor/github.com/AlecAivazis/survey/v2/terminal/cursor_windows.go
generated
vendored
|
@ -1,138 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var COORDINATE_SYSTEM_BEGIN Short = 0
|
||||
|
||||
// shared variable to save the cursor location from CursorSave()
|
||||
var cursorLoc Coord
|
||||
|
||||
type Cursor struct {
|
||||
In FileReader
|
||||
Out FileWriter
|
||||
}
|
||||
|
||||
func (c *Cursor) Up(n int) {
|
||||
c.cursorMove(0, n)
|
||||
}
|
||||
|
||||
func (c *Cursor) Down(n int) {
|
||||
c.cursorMove(0, -1*n)
|
||||
}
|
||||
|
||||
func (c *Cursor) Forward(n int) {
|
||||
c.cursorMove(n, 0)
|
||||
}
|
||||
|
||||
func (c *Cursor) Back(n int) {
|
||||
c.cursorMove(-1*n, 0)
|
||||
}
|
||||
|
||||
// save the cursor location
|
||||
func (c *Cursor) Save() {
|
||||
cursorLoc, _ = c.Location(nil)
|
||||
}
|
||||
|
||||
func (c *Cursor) Restore() {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
// restore it to the original position
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursorLoc))))
|
||||
}
|
||||
|
||||
func (cur Coord) CursorIsAtLineEnd(size *Coord) bool {
|
||||
return cur.X == size.X
|
||||
}
|
||||
|
||||
func (cur Coord) CursorIsAtLineBegin() bool {
|
||||
return cur.X == 0
|
||||
}
|
||||
|
||||
func (c *Cursor) cursorMove(x int, y int) {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
var cursor Coord
|
||||
cursor.X = csbi.cursorPosition.X + Short(x)
|
||||
cursor.Y = csbi.cursorPosition.Y + Short(y)
|
||||
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor))))
|
||||
}
|
||||
|
||||
func (c *Cursor) NextLine(n int) {
|
||||
c.Up(n)
|
||||
c.HorizontalAbsolute(0)
|
||||
}
|
||||
|
||||
func (c *Cursor) PreviousLine(n int) {
|
||||
c.Down(n)
|
||||
c.HorizontalAbsolute(0)
|
||||
}
|
||||
|
||||
// for comparability purposes between windows
|
||||
// in windows we don't have to print out a new line
|
||||
func (c *Cursor) MoveNextLine(cur Coord, terminalSize *Coord) {
|
||||
c.NextLine(1)
|
||||
}
|
||||
|
||||
func (c *Cursor) HorizontalAbsolute(x int) {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
var cursor Coord
|
||||
cursor.X = Short(x)
|
||||
cursor.Y = csbi.cursorPosition.Y
|
||||
|
||||
if csbi.size.X < cursor.X {
|
||||
cursor.X = csbi.size.X
|
||||
}
|
||||
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor))))
|
||||
}
|
||||
|
||||
func (c *Cursor) Show() {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var cci consoleCursorInfo
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
|
||||
cci.visible = 1
|
||||
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
|
||||
}
|
||||
|
||||
func (c *Cursor) Hide() {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var cci consoleCursorInfo
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
|
||||
cci.visible = 0
|
||||
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci)))
|
||||
}
|
||||
|
||||
func (c *Cursor) Location(buf *bytes.Buffer) (Coord, error) {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
return csbi.cursorPosition, nil
|
||||
}
|
||||
|
||||
func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) {
|
||||
handle := syscall.Handle(c.Out.Fd())
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
// windows' coordinate system begins at (0, 0)
|
||||
csbi.size.X--
|
||||
csbi.size.Y--
|
||||
return &csbi.size, nil
|
||||
}
|
9
vendor/github.com/AlecAivazis/survey/v2/terminal/display.go
generated
vendored
9
vendor/github.com/AlecAivazis/survey/v2/terminal/display.go
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
package terminal
|
||||
|
||||
type EraseLineMode int
|
||||
|
||||
const (
|
||||
ERASE_LINE_END EraseLineMode = iota
|
||||
ERASE_LINE_START
|
||||
ERASE_LINE_ALL
|
||||
)
|
11
vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go
generated
vendored
11
vendor/github.com/AlecAivazis/survey/v2/terminal/display_posix.go
generated
vendored
|
@ -1,11 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func EraseLine(out FileWriter, mode EraseLineMode) {
|
||||
fmt.Fprintf(out, "\x1b[%dK", mode)
|
||||
}
|
27
vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go
generated
vendored
27
vendor/github.com/AlecAivazis/survey/v2/terminal/display_windows.go
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func EraseLine(out FileWriter, mode EraseLineMode) {
|
||||
handle := syscall.Handle(out.Fd())
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
var w uint32
|
||||
var x Short
|
||||
cursor := csbi.cursorPosition
|
||||
switch mode {
|
||||
case ERASE_LINE_END:
|
||||
x = csbi.size.X
|
||||
case ERASE_LINE_START:
|
||||
x = 0
|
||||
case ERASE_LINE_ALL:
|
||||
cursor.X = 0
|
||||
x = csbi.size.X
|
||||
}
|
||||
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(x), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w)))
|
||||
}
|
9
vendor/github.com/AlecAivazis/survey/v2/terminal/error.go
generated
vendored
9
vendor/github.com/AlecAivazis/survey/v2/terminal/error.go
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
InterruptErr = errors.New("interrupt")
|
||||
)
|
19
vendor/github.com/AlecAivazis/survey/v2/terminal/output.go
generated
vendored
19
vendor/github.com/AlecAivazis/survey/v2/terminal/output.go
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// NewAnsiStdout returns special stdout, which converts escape sequences to Windows API calls
|
||||
// on Windows environment.
|
||||
func NewAnsiStdout(out FileWriter) io.Writer {
|
||||
return out
|
||||
}
|
||||
|
||||
// NewAnsiStderr returns special stderr, which converts escape sequences to Windows API calls
|
||||
// on Windows environment.
|
||||
func NewAnsiStderr(out FileWriter) io.Writer {
|
||||
return out
|
||||
}
|
227
vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go
generated
vendored
227
vendor/github.com/AlecAivazis/survey/v2/terminal/output_windows.go
generated
vendored
|
@ -1,227 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
cursorFunctions = map[rune]func(c *Cursor) func(int){
|
||||
'A': func(c *Cursor) func(int) { return c.Up },
|
||||
'B': func(c *Cursor) func(int) { return c.Down },
|
||||
'C': func(c *Cursor) func(int) { return c.Forward },
|
||||
'D': func(c *Cursor) func(int) { return c.Back },
|
||||
'E': func(c *Cursor) func(int) { return c.NextLine },
|
||||
'F': func(c *Cursor) func(int) { return c.PreviousLine },
|
||||
'G': func(c *Cursor) func(int) { return c.HorizontalAbsolute },
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = 0x1
|
||||
foregroundGreen = 0x2
|
||||
foregroundRed = 0x4
|
||||
foregroundIntensity = 0x8
|
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||
backgroundBlue = 0x10
|
||||
backgroundGreen = 0x20
|
||||
backgroundRed = 0x40
|
||||
backgroundIntensity = 0x80
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
out FileWriter
|
||||
handle syscall.Handle
|
||||
orgAttr word
|
||||
}
|
||||
|
||||
func NewAnsiStdout(out FileWriter) io.Writer {
|
||||
var csbi consoleScreenBufferInfo
|
||||
if !isatty.IsTerminal(out.Fd()) {
|
||||
return out
|
||||
}
|
||||
handle := syscall.Handle(out.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: out, handle: handle, orgAttr: csbi.attributes}
|
||||
}
|
||||
|
||||
func NewAnsiStderr(out FileWriter) io.Writer {
|
||||
var csbi consoleScreenBufferInfo
|
||||
if !isatty.IsTerminal(out.Fd()) {
|
||||
return out
|
||||
}
|
||||
handle := syscall.Handle(out.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: out, handle: handle, orgAttr: csbi.attributes}
|
||||
}
|
||||
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
for {
|
||||
ch, size, err := r.ReadRune()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
n += size
|
||||
|
||||
switch ch {
|
||||
case '\x1b':
|
||||
size, err = w.handleEscape(r)
|
||||
n += size
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(w.out, string(ch))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) handleEscape(r *bytes.Reader) (n int, err error) {
|
||||
buf := make([]byte, 0, 10)
|
||||
buf = append(buf, "\x1b"...)
|
||||
|
||||
// Check '[' continues after \x1b
|
||||
ch, size, err := r.ReadRune()
|
||||
if err != nil {
|
||||
fmt.Fprint(w.out, string(buf))
|
||||
return
|
||||
}
|
||||
n += size
|
||||
if ch != '[' {
|
||||
fmt.Fprint(w.out, string(buf))
|
||||
return
|
||||
}
|
||||
|
||||
// Parse escape code
|
||||
var code rune
|
||||
argBuf := make([]byte, 0, 10)
|
||||
for {
|
||||
ch, size, err = r.ReadRune()
|
||||
if err != nil {
|
||||
fmt.Fprint(w.out, string(buf))
|
||||
return
|
||||
}
|
||||
n += size
|
||||
if ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') {
|
||||
code = ch
|
||||
break
|
||||
}
|
||||
argBuf = append(argBuf, string(ch)...)
|
||||
}
|
||||
|
||||
w.applyEscapeCode(buf, string(argBuf), code)
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) applyEscapeCode(buf []byte, arg string, code rune) {
|
||||
c := &Cursor{Out: w.out}
|
||||
|
||||
switch arg + string(code) {
|
||||
case "?25h":
|
||||
c.Show()
|
||||
return
|
||||
case "?25l":
|
||||
c.Hide()
|
||||
return
|
||||
}
|
||||
|
||||
if f, ok := cursorFunctions[code]; ok {
|
||||
if n, err := strconv.Atoi(arg); err == nil {
|
||||
f(c)(n)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch code {
|
||||
case 'm':
|
||||
w.applySelectGraphicRendition(arg)
|
||||
default:
|
||||
buf = append(buf, string(code)...)
|
||||
fmt.Fprint(w.out, string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
// Original implementation: https://github.com/mattn/go-colorable
|
||||
func (w *Writer) applySelectGraphicRendition(arg string) {
|
||||
if arg == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.orgAttr))
|
||||
return
|
||||
}
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
attr := csbi.attributes
|
||||
|
||||
for _, param := range strings.Split(arg, ";") {
|
||||
n, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case n == 0 || n == 100:
|
||||
attr = w.orgAttr
|
||||
case 1 <= n && n <= 5:
|
||||
attr |= foregroundIntensity
|
||||
case 30 <= n && n <= 37:
|
||||
attr = (attr & backgroundMask)
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-30)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-30)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case 40 <= n && n <= 47:
|
||||
attr = (attr & foregroundMask)
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-40)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-40)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
case 90 <= n && n <= 97:
|
||||
attr = (attr & backgroundMask)
|
||||
attr |= foregroundIntensity
|
||||
if (n-90)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-90)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-90)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case 100 <= n && n <= 107:
|
||||
attr = (attr & foregroundMask)
|
||||
attr |= backgroundIntensity
|
||||
if (n-100)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-100)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-100)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
}
|
373
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go
generated
vendored
373
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader.go
generated
vendored
|
@ -1,373 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
type RuneReader struct {
|
||||
stdio Stdio
|
||||
cursor *Cursor
|
||||
state runeReaderState
|
||||
}
|
||||
|
||||
func NewRuneReader(stdio Stdio) *RuneReader {
|
||||
return &RuneReader{
|
||||
stdio: stdio,
|
||||
state: newRuneReaderState(stdio.In),
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RuneReader) printChar(char rune, mask rune) {
|
||||
// if we don't need to mask the input
|
||||
if mask == 0 {
|
||||
// just print the character the user pressed
|
||||
fmt.Fprintf(rr.stdio.Out, "%c", char)
|
||||
} else {
|
||||
// otherwise print the mask we were given
|
||||
fmt.Fprintf(rr.stdio.Out, "%c", mask)
|
||||
}
|
||||
}
|
||||
|
||||
type OnRuneFn func(rune, []rune) ([]rune, bool, error)
|
||||
|
||||
func (rr *RuneReader) ReadLine(mask rune, onRunes ...OnRuneFn) ([]rune, error) {
|
||||
return rr.ReadLineWithDefault(mask, []rune{}, onRunes...)
|
||||
}
|
||||
|
||||
func (rr *RuneReader) ReadLineWithDefault(mask rune, d []rune, onRunes ...OnRuneFn) ([]rune, error) {
|
||||
line := []rune{}
|
||||
// we only care about horizontal displacements from the origin so start counting at 0
|
||||
index := 0
|
||||
|
||||
cursor := &Cursor{
|
||||
In: rr.stdio.In,
|
||||
Out: rr.stdio.Out,
|
||||
}
|
||||
|
||||
onRune := func(r rune, line []rune) ([]rune, bool, error) {
|
||||
return line, false, nil
|
||||
}
|
||||
|
||||
// if the user pressed a key the caller was interested in capturing
|
||||
if len(onRunes) > 0 {
|
||||
onRune = onRunes[0]
|
||||
}
|
||||
|
||||
// we get the terminal width and height (if resized after this point the property might become invalid)
|
||||
terminalSize, _ := cursor.Size(rr.Buffer())
|
||||
// we set the current location of the cursor once
|
||||
cursorCurrent, _ := cursor.Location(rr.Buffer())
|
||||
|
||||
increment := func() {
|
||||
if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
|
||||
cursorCurrent.X = COORDINATE_SYSTEM_BEGIN
|
||||
cursorCurrent.Y++
|
||||
} else {
|
||||
cursorCurrent.X++
|
||||
}
|
||||
}
|
||||
decrement := func() {
|
||||
if cursorCurrent.CursorIsAtLineBegin() {
|
||||
cursorCurrent.X = terminalSize.X
|
||||
cursorCurrent.Y--
|
||||
} else {
|
||||
cursorCurrent.X--
|
||||
}
|
||||
}
|
||||
|
||||
if len(d) > 0 {
|
||||
index = len(d)
|
||||
fmt.Fprint(rr.stdio.Out, string(d))
|
||||
line = d
|
||||
for range d {
|
||||
increment()
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
// wait for some input
|
||||
r, _, err := rr.ReadRune()
|
||||
if err != nil {
|
||||
return line, err
|
||||
}
|
||||
|
||||
if l, stop, err := onRune(r, line); stop || err != nil {
|
||||
return l, err
|
||||
}
|
||||
|
||||
// if the user pressed enter or some other newline/termination like ctrl+d
|
||||
if r == '\r' || r == '\n' || r == KeyEndTransmission {
|
||||
// delete what's printed out on the console screen (cleanup)
|
||||
for index > 0 {
|
||||
if cursorCurrent.CursorIsAtLineBegin() {
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
cursor.PreviousLine(1)
|
||||
cursor.Forward(int(terminalSize.X))
|
||||
} else {
|
||||
cursor.Back(1)
|
||||
}
|
||||
decrement()
|
||||
index--
|
||||
}
|
||||
// move the cursor the a new line
|
||||
cursor.MoveNextLine(cursorCurrent, terminalSize)
|
||||
|
||||
// we're done processing the input
|
||||
return line, nil
|
||||
}
|
||||
// if the user interrupts (ie with ctrl+c)
|
||||
if r == KeyInterrupt {
|
||||
// go to the beginning of the next line
|
||||
fmt.Fprint(rr.stdio.Out, "\r\n")
|
||||
|
||||
// we're done processing the input, and treat interrupt like an error
|
||||
return line, InterruptErr
|
||||
}
|
||||
|
||||
// allow for backspace/delete editing of inputs
|
||||
if r == KeyBackspace || r == KeyDelete {
|
||||
// and we're not at the beginning of the line
|
||||
if index > 0 && len(line) > 0 {
|
||||
// if we are at the end of the word
|
||||
if index == len(line) {
|
||||
// just remove the last letter from the internal representation
|
||||
// also count the number of cells the rune before the cursor occupied
|
||||
cells := runeWidth(line[len(line)-1])
|
||||
line = line[:len(line)-1]
|
||||
// go back one
|
||||
if cursorCurrent.X == 1 {
|
||||
cursor.PreviousLine(1)
|
||||
cursor.Forward(int(terminalSize.X))
|
||||
} else {
|
||||
cursor.Back(cells)
|
||||
}
|
||||
|
||||
// clear the rest of the line
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
} else {
|
||||
// we need to remove a character from the middle of the word
|
||||
|
||||
cells := runeWidth(line[index-1])
|
||||
|
||||
// remove the current index from the list
|
||||
line = append(line[:index-1], line[index:]...)
|
||||
|
||||
// save the current position of the cursor, as we have to move the cursor one back to erase the current symbol
|
||||
// and then move the cursor for each symbol in line[index-1:] to print it out, afterwards we want to restore
|
||||
// the cursor to its previous location.
|
||||
cursor.Save()
|
||||
|
||||
// clear the rest of the line
|
||||
cursor.Back(cells)
|
||||
|
||||
// print what comes after
|
||||
for _, char := range line[index-1:] {
|
||||
//Erase symbols which are left over from older print
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
// print characters to the new line appropriately
|
||||
rr.printChar(char, mask)
|
||||
|
||||
}
|
||||
// erase what's left over from last print
|
||||
if cursorCurrent.Y < terminalSize.Y {
|
||||
cursor.NextLine(1)
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
}
|
||||
// restore cursor
|
||||
cursor.Restore()
|
||||
if cursorCurrent.CursorIsAtLineBegin() {
|
||||
cursor.PreviousLine(1)
|
||||
cursor.Forward(int(terminalSize.X))
|
||||
} else {
|
||||
cursor.Back(cells)
|
||||
}
|
||||
}
|
||||
|
||||
// decrement the index
|
||||
index--
|
||||
decrement()
|
||||
} else {
|
||||
// otherwise the user pressed backspace while at the beginning of the line
|
||||
soundBell(rr.stdio.Out)
|
||||
}
|
||||
|
||||
// we're done processing this key
|
||||
continue
|
||||
}
|
||||
|
||||
// if the left arrow is pressed
|
||||
if r == KeyArrowLeft {
|
||||
// if we have space to the left
|
||||
if index > 0 {
|
||||
//move the cursor to the prev line if necessary
|
||||
if cursorCurrent.CursorIsAtLineBegin() {
|
||||
cursor.PreviousLine(1)
|
||||
cursor.Forward(int(terminalSize.X))
|
||||
} else {
|
||||
cursor.Back(runeWidth(line[index-1]))
|
||||
}
|
||||
//decrement the index
|
||||
index--
|
||||
decrement()
|
||||
|
||||
} else {
|
||||
// otherwise we are at the beginning of where we started reading lines
|
||||
// sound the bell
|
||||
soundBell(rr.stdio.Out)
|
||||
}
|
||||
|
||||
// we're done processing this key press
|
||||
continue
|
||||
}
|
||||
|
||||
// if the right arrow is pressed
|
||||
if r == KeyArrowRight {
|
||||
// if we have space to the right
|
||||
if index < len(line) {
|
||||
// move the cursor to the next line if necessary
|
||||
if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
|
||||
cursor.NextLine(1)
|
||||
} else {
|
||||
cursor.Forward(runeWidth(line[index]))
|
||||
}
|
||||
index++
|
||||
increment()
|
||||
|
||||
} else {
|
||||
// otherwise we are at the end of the word and can't go past
|
||||
// sound the bell
|
||||
soundBell(rr.stdio.Out)
|
||||
}
|
||||
|
||||
// we're done processing this key press
|
||||
continue
|
||||
}
|
||||
// the user pressed one of the special keys
|
||||
if r == SpecialKeyHome {
|
||||
for index > 0 {
|
||||
if cursorCurrent.CursorIsAtLineBegin() {
|
||||
cursor.PreviousLine(1)
|
||||
cursor.Forward(int(terminalSize.X))
|
||||
cursorCurrent.Y--
|
||||
cursorCurrent.X = terminalSize.X
|
||||
} else {
|
||||
cursor.Back(runeWidth(line[index-1]))
|
||||
cursorCurrent.X -= Short(runeWidth(line[index-1]))
|
||||
}
|
||||
index--
|
||||
}
|
||||
continue
|
||||
// user pressed end
|
||||
} else if r == SpecialKeyEnd {
|
||||
for index != len(line) {
|
||||
if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
|
||||
cursor.NextLine(1)
|
||||
cursorCurrent.Y++
|
||||
cursorCurrent.X = COORDINATE_SYSTEM_BEGIN
|
||||
} else {
|
||||
cursor.Forward(runeWidth(line[index]))
|
||||
cursorCurrent.X += Short(runeWidth(line[index]))
|
||||
}
|
||||
index++
|
||||
}
|
||||
continue
|
||||
// user pressed forward delete key
|
||||
} else if r == SpecialKeyDelete {
|
||||
// if index at the end of the line nothing to delete
|
||||
if index != len(line) {
|
||||
// save the current position of the cursor, as we have to erase the current symbol
|
||||
// and then move the cursor for each symbol in line[index:] to print it out, afterwards we want to restore
|
||||
// the cursor to its previous location.
|
||||
cursor.Save()
|
||||
// remove the symbol after the cursor
|
||||
line = append(line[:index], line[index+1:]...)
|
||||
// print the updated line
|
||||
for _, char := range line[index:] {
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
// print out the character
|
||||
rr.printChar(char, mask)
|
||||
}
|
||||
// erase what's left on last line
|
||||
if cursorCurrent.Y < terminalSize.Y {
|
||||
cursor.NextLine(1)
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
}
|
||||
// restore cursor
|
||||
cursor.Restore()
|
||||
if len(line) == 0 || index == len(line) {
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// if the letter is another escape sequence
|
||||
if unicode.IsControl(r) || r == IgnoreKey {
|
||||
// ignore it
|
||||
continue
|
||||
}
|
||||
|
||||
// the user pressed a regular key
|
||||
|
||||
// if we are at the end of the line
|
||||
if index == len(line) {
|
||||
// just append the character at the end of the line
|
||||
line = append(line, r)
|
||||
// save the location of the cursor
|
||||
index++
|
||||
increment()
|
||||
// print out the character
|
||||
rr.printChar(r, mask)
|
||||
} else {
|
||||
// we are in the middle of the word so we need to insert the character the user pressed
|
||||
line = append(line[:index], append([]rune{r}, line[index:]...)...)
|
||||
// save the current position of the cursor, as we have to move the cursor back to erase the current symbol
|
||||
// and then move for each symbol in line[index:] to print it out, afterwards we want to restore
|
||||
// cursor's location to its previous one.
|
||||
cursor.Save()
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
// remove the symbol after the cursor
|
||||
// print the updated line
|
||||
for _, char := range line[index:] {
|
||||
EraseLine(rr.stdio.Out, ERASE_LINE_END)
|
||||
// print out the character
|
||||
rr.printChar(char, mask)
|
||||
increment()
|
||||
}
|
||||
// if we are at the last line, we want to visually insert a new line and append to it.
|
||||
if cursorCurrent.CursorIsAtLineEnd(terminalSize) && cursorCurrent.Y == terminalSize.Y {
|
||||
// add a new line to the terminal
|
||||
fmt.Fprintln(rr.stdio.Out)
|
||||
// restore the position of the cursor horizontally
|
||||
cursor.Restore()
|
||||
// restore the position of the cursor vertically
|
||||
cursor.PreviousLine(1)
|
||||
} else {
|
||||
// restore cursor
|
||||
cursor.Restore()
|
||||
}
|
||||
// check if cursor needs to move to next line
|
||||
cursorCurrent, _ = cursor.Location(rr.Buffer())
|
||||
if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
|
||||
cursor.NextLine(1)
|
||||
} else {
|
||||
cursor.Forward(runeWidth(r))
|
||||
}
|
||||
// increment the index
|
||||
index++
|
||||
increment()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runeWidth(r rune) int {
|
||||
switch width.LookupRune(r).Kind() {
|
||||
case width.EastAsianWide, width.EastAsianFullwidth:
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
13
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go
generated
vendored
13
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_bsd.go
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
// copied from: https://github.com/golang/crypto/blob/master/ssh/terminal/util_bsd.go
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package terminal
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
const ioctlWriteTermios = syscall.TIOCSETA
|
13
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go
generated
vendored
13
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_linux.go
generated
vendored
|
@ -1,13 +0,0 @@
|
|||
// copied from https://github.com/golang/crypto/blob/master/ssh/terminal/util_linux.go
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build linux,!ppc64le
|
||||
|
||||
package terminal
|
||||
|
||||
// These constants are declared here, rather than importing
|
||||
// them from the syscall package as some syscall packages, even
|
||||
// on linux, for example gccgo, do not declare them.
|
||||
const ioctlReadTermios = 0x5401 // syscall.TCGETS
|
||||
const ioctlWriteTermios = 0x5402 // syscall.TCSETS
|
111
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go
generated
vendored
111
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_posix.go
generated
vendored
|
@ -1,111 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
// The terminal mode manipulation code is derived heavily from:
|
||||
// https://github.com/golang/crypto/blob/master/ssh/terminal/util.go:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type runeReaderState struct {
|
||||
term syscall.Termios
|
||||
reader *bufio.Reader
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func newRuneReaderState(input FileReader) runeReaderState {
|
||||
buf := new(bytes.Buffer)
|
||||
return runeReaderState{
|
||||
reader: bufio.NewReader(&BufferedReader{
|
||||
In: input,
|
||||
Buffer: buf,
|
||||
}),
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RuneReader) Buffer() *bytes.Buffer {
|
||||
return rr.state.buf
|
||||
}
|
||||
|
||||
// For reading runes we just want to disable echo.
|
||||
func (rr *RuneReader) SetTermMode() error {
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
newState := rr.state.term
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG
|
||||
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RuneReader) RestoreTermMode() error {
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(rr.stdio.In.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&rr.state.term)), 0, 0, 0); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RuneReader) ReadRune() (rune, int, error) {
|
||||
r, size, err := rr.state.reader.ReadRune()
|
||||
if err != nil {
|
||||
return r, size, err
|
||||
}
|
||||
|
||||
// parse ^[ sequences to look for arrow keys
|
||||
if r == '\033' {
|
||||
if rr.state.reader.Buffered() == 0 {
|
||||
// no more characters so must be `Esc` key
|
||||
return KeyEscape, 1, nil
|
||||
}
|
||||
r, size, err = rr.state.reader.ReadRune()
|
||||
if err != nil {
|
||||
return r, size, err
|
||||
}
|
||||
if r != '[' {
|
||||
return r, size, fmt.Errorf("Unexpected Escape Sequence: %q", []rune{'\033', r})
|
||||
}
|
||||
r, size, err = rr.state.reader.ReadRune()
|
||||
if err != nil {
|
||||
return r, size, err
|
||||
}
|
||||
switch r {
|
||||
case 'D':
|
||||
return KeyArrowLeft, 1, nil
|
||||
case 'C':
|
||||
return KeyArrowRight, 1, nil
|
||||
case 'A':
|
||||
return KeyArrowUp, 1, nil
|
||||
case 'B':
|
||||
return KeyArrowDown, 1, nil
|
||||
case 'H': // Home button
|
||||
return SpecialKeyHome, 1, nil
|
||||
case 'F': // End button
|
||||
return SpecialKeyEnd, 1, nil
|
||||
case '3': // Delete Button
|
||||
// discard the following '~' key from buffer
|
||||
rr.state.reader.Discard(1)
|
||||
return SpecialKeyDelete, 1, nil
|
||||
default:
|
||||
// discard the following '~' key from buffer
|
||||
rr.state.reader.Discard(1)
|
||||
return IgnoreKey, 1, nil
|
||||
}
|
||||
return r, size, fmt.Errorf("Unknown Escape Sequence: %q", []rune{'\033', '[', r})
|
||||
}
|
||||
return r, size, err
|
||||
}
|
7
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go
generated
vendored
7
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_ppc64le.go
generated
vendored
|
@ -1,7 +0,0 @@
|
|||
// +build ppc64le,linux
|
||||
|
||||
package terminal
|
||||
|
||||
// Used syscall numbers from https://github.com/golang/go/blob/master/src/syscall/ztypes_linux_ppc64le.go
|
||||
const ioctlReadTermios = 0x402c7413 // syscall.TCGETS
|
||||
const ioctlWriteTermios = 0x802c7414 // syscall.TCSETS
|
142
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go
generated
vendored
142
vendor/github.com/AlecAivazis/survey/v2/terminal/runereader_windows.go
generated
vendored
|
@ -1,142 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
dll = syscall.NewLazyDLL("kernel32.dll")
|
||||
setConsoleMode = dll.NewProc("SetConsoleMode")
|
||||
getConsoleMode = dll.NewProc("GetConsoleMode")
|
||||
readConsoleInput = dll.NewProc("ReadConsoleInputW")
|
||||
)
|
||||
|
||||
const (
|
||||
EVENT_KEY = 0x0001
|
||||
|
||||
// key codes for arrow keys
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
VK_DELETE = 0x2E
|
||||
VK_END = 0x23
|
||||
VK_HOME = 0x24
|
||||
VK_LEFT = 0x25
|
||||
VK_UP = 0x26
|
||||
VK_RIGHT = 0x27
|
||||
VK_DOWN = 0x28
|
||||
|
||||
RIGHT_CTRL_PRESSED = 0x0004
|
||||
LEFT_CTRL_PRESSED = 0x0008
|
||||
|
||||
ENABLE_ECHO_INPUT uint32 = 0x0004
|
||||
ENABLE_LINE_INPUT uint32 = 0x0002
|
||||
ENABLE_PROCESSED_INPUT uint32 = 0x0001
|
||||
)
|
||||
|
||||
type inputRecord struct {
|
||||
eventType uint16
|
||||
padding uint16
|
||||
event [16]byte
|
||||
}
|
||||
|
||||
type keyEventRecord struct {
|
||||
bKeyDown int32
|
||||
wRepeatCount uint16
|
||||
wVirtualKeyCode uint16
|
||||
wVirtualScanCode uint16
|
||||
unicodeChar uint16
|
||||
wdControlKeyState uint32
|
||||
}
|
||||
|
||||
type runeReaderState struct {
|
||||
term uint32
|
||||
}
|
||||
|
||||
func newRuneReaderState(input FileReader) runeReaderState {
|
||||
return runeReaderState{}
|
||||
}
|
||||
|
||||
func (rr *RuneReader) Buffer() *bytes.Buffer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RuneReader) SetTermMode() error {
|
||||
r, _, err := getConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(unsafe.Pointer(&rr.state.term)))
|
||||
// windows return 0 on error
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
newState := rr.state.term
|
||||
newState &^= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT
|
||||
r, _, err = setConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(newState))
|
||||
// windows return 0 on error
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RuneReader) RestoreTermMode() error {
|
||||
r, _, err := setConsoleMode.Call(uintptr(rr.stdio.In.Fd()), uintptr(rr.state.term))
|
||||
// windows return 0 on error
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *RuneReader) ReadRune() (rune, int, error) {
|
||||
ir := &inputRecord{}
|
||||
bytesRead := 0
|
||||
for {
|
||||
rv, _, e := readConsoleInput.Call(rr.stdio.In.Fd(), uintptr(unsafe.Pointer(ir)), 1, uintptr(unsafe.Pointer(&bytesRead)))
|
||||
// windows returns non-zero to indicate success
|
||||
if rv == 0 && e != nil {
|
||||
return 0, 0, e
|
||||
}
|
||||
|
||||
if ir.eventType != EVENT_KEY {
|
||||
continue
|
||||
}
|
||||
|
||||
// the event data is really a c struct union, so here we have to do an usafe
|
||||
// cast to put the data into the keyEventRecord (since we have already verified
|
||||
// above that this event does correspond to a key event
|
||||
key := (*keyEventRecord)(unsafe.Pointer(&ir.event[0]))
|
||||
// we only care about key down events
|
||||
if key.bKeyDown == 0 {
|
||||
continue
|
||||
}
|
||||
if key.wdControlKeyState&(LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED) != 0 && key.unicodeChar == 'C' {
|
||||
return KeyInterrupt, bytesRead, nil
|
||||
}
|
||||
// not a normal character so look up the input sequence from the
|
||||
// virtual key code mappings (VK_*)
|
||||
if key.unicodeChar == 0 {
|
||||
switch key.wVirtualKeyCode {
|
||||
case VK_DOWN:
|
||||
return KeyArrowDown, bytesRead, nil
|
||||
case VK_LEFT:
|
||||
return KeyArrowLeft, bytesRead, nil
|
||||
case VK_RIGHT:
|
||||
return KeyArrowRight, bytesRead, nil
|
||||
case VK_UP:
|
||||
return KeyArrowUp, bytesRead, nil
|
||||
case VK_DELETE:
|
||||
return SpecialKeyDelete, bytesRead, nil
|
||||
case VK_HOME:
|
||||
return SpecialKeyHome, bytesRead, nil
|
||||
case VK_END:
|
||||
return SpecialKeyEnd, bytesRead, nil
|
||||
default:
|
||||
// not a virtual key that we care about so just continue on to
|
||||
// the next input key
|
||||
continue
|
||||
}
|
||||
}
|
||||
r := rune(key.unicodeChar)
|
||||
return r, bytesRead, nil
|
||||
}
|
||||
}
|
31
vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go
generated
vendored
31
vendor/github.com/AlecAivazis/survey/v2/terminal/sequences.go
generated
vendored
|
@ -1,31 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
KeyArrowLeft = '\x02'
|
||||
KeyArrowRight = '\x06'
|
||||
KeyArrowUp = '\x10'
|
||||
KeyArrowDown = '\x0e'
|
||||
KeySpace = ' '
|
||||
KeyEnter = '\r'
|
||||
KeyBackspace = '\b'
|
||||
KeyDelete = '\x7f'
|
||||
KeyInterrupt = '\x03'
|
||||
KeyEndTransmission = '\x04'
|
||||
KeyEscape = '\x1b'
|
||||
KeyDeleteWord = '\x17' // Ctrl+W
|
||||
KeyDeleteLine = '\x18' // Ctrl+X
|
||||
SpecialKeyHome = '\x01'
|
||||
SpecialKeyEnd = '\x11'
|
||||
SpecialKeyDelete = '\x12'
|
||||
IgnoreKey = '\000'
|
||||
KeyTab = '\t'
|
||||
)
|
||||
|
||||
func soundBell(out io.Writer) {
|
||||
fmt.Fprint(out, "\a")
|
||||
}
|
24
vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go
generated
vendored
24
vendor/github.com/AlecAivazis/survey/v2/terminal/stdio.go
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Stdio is the standard input/output the terminal reads/writes with.
|
||||
type Stdio struct {
|
||||
In FileReader
|
||||
Out FileWriter
|
||||
Err io.Writer
|
||||
}
|
||||
|
||||
// FileWriter provides a minimal interface for Stdin.
|
||||
type FileWriter interface {
|
||||
io.Writer
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// FileReader provides a minimal interface for Stdout.
|
||||
type FileReader interface {
|
||||
io.Reader
|
||||
Fd() uintptr
|
||||
}
|
39
vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go
generated
vendored
39
vendor/github.com/AlecAivazis/survey/v2/terminal/syscall_windows.go
generated
vendored
|
@ -1,39 +0,0 @@
|
|||
package terminal
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type smallRect struct {
|
||||
left Short
|
||||
top Short
|
||||
right Short
|
||||
bottom Short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size Coord
|
||||
cursorPosition Coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize Coord
|
||||
}
|
||||
|
||||
type consoleCursorInfo struct {
|
||||
size dword
|
||||
visible int32
|
||||
}
|
8
vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go
generated
vendored
8
vendor/github.com/AlecAivazis/survey/v2/terminal/terminal.go
generated
vendored
|
@ -1,8 +0,0 @@
|
|||
package terminal
|
||||
|
||||
type Short int16
|
||||
|
||||
type Coord struct {
|
||||
X Short
|
||||
Y Short
|
||||
}
|
79
vendor/github.com/AlecAivazis/survey/v2/transform.go
generated
vendored
79
vendor/github.com/AlecAivazis/survey/v2/transform.go
generated
vendored
|
@ -1,79 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TransformString returns a `Transformer` based on the "f"
|
||||
// function which accepts a string representation of the answer
|
||||
// and returns a new one, transformed, answer.
|
||||
// Take for example the functions inside the std `strings` package,
|
||||
// they can be converted to a compatible `Transformer` by using this function,
|
||||
// i.e: `TransformString(strings.Title)`, `TransformString(strings.ToUpper)`.
|
||||
//
|
||||
// Note that `TransformString` is just a helper, `Transformer` can be used
|
||||
// to transform any type of answer.
|
||||
func TransformString(f func(s string) string) Transformer {
|
||||
return func(ans interface{}) interface{} {
|
||||
// if the answer value passed in is the zero value of the appropriate type
|
||||
if isZero(reflect.ValueOf(ans)) {
|
||||
// skip this `Transformer` by returning a zero value of string.
|
||||
// The original answer will be not affected,
|
||||
// see survey.go#L125.
|
||||
// A zero value of string should be returned to be handled by
|
||||
// next Transformer in a composed Tranformer,
|
||||
// see tranform.go#L75
|
||||
return ""
|
||||
}
|
||||
|
||||
// "ans" is never nil here, so we don't have to check that
|
||||
// see survey.go#L338 for more.
|
||||
// Make sure that the the answer's value was a typeof string.
|
||||
s, ok := ans.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return f(s)
|
||||
}
|
||||
}
|
||||
|
||||
// ToLower is a `Transformer`.
|
||||
// It receives an answer value
|
||||
// and returns a copy of the "ans"
|
||||
// with all Unicode letters mapped to their lower case.
|
||||
//
|
||||
// Note that if "ans" is not a string then it will
|
||||
// return a nil value, meaning that the above answer
|
||||
// will not be affected by this call at all.
|
||||
func ToLower(ans interface{}) interface{} {
|
||||
transformer := TransformString(strings.ToLower)
|
||||
return transformer(ans)
|
||||
}
|
||||
|
||||
// Title is a `Transformer`.
|
||||
// It receives an answer value
|
||||
// and returns a copy of the "ans"
|
||||
// with all Unicode letters that begin words
|
||||
// mapped to their title case.
|
||||
//
|
||||
// Note that if "ans" is not a string then it will
|
||||
// return a nil value, meaning that the above answer
|
||||
// will not be affected by this call at all.
|
||||
func Title(ans interface{}) interface{} {
|
||||
transformer := TransformString(strings.Title)
|
||||
return transformer(ans)
|
||||
}
|
||||
|
||||
// ComposeTransformers is a variadic function used to create one transformer from many.
|
||||
func ComposeTransformers(transformers ...Transformer) Transformer {
|
||||
// return a transformer that calls each one sequentially
|
||||
return func(ans interface{}) interface{} {
|
||||
// execute each transformer
|
||||
for _, t := range transformers {
|
||||
ans = t(ans)
|
||||
}
|
||||
return ans
|
||||
}
|
||||
}
|
127
vendor/github.com/AlecAivazis/survey/v2/validate.go
generated
vendored
127
vendor/github.com/AlecAivazis/survey/v2/validate.go
generated
vendored
|
@ -1,127 +0,0 @@
|
|||
package survey
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2/core"
|
||||
)
|
||||
|
||||
// Required does not allow an empty value
|
||||
func Required(val interface{}) error {
|
||||
// the reflect value of the result
|
||||
value := reflect.ValueOf(val)
|
||||
|
||||
// if the value passed in is the zero value of the appropriate type
|
||||
if isZero(value) && value.Kind() != reflect.Bool {
|
||||
return errors.New("Value is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxLength requires that the string is no longer than the specified value
|
||||
func MaxLength(length int) Validator {
|
||||
// return a validator that checks the length of the string
|
||||
return func(val interface{}) error {
|
||||
if str, ok := val.(string); ok {
|
||||
// if the string is longer than the given value
|
||||
if len([]rune(str)) > length {
|
||||
// yell loudly
|
||||
return fmt.Errorf("value is too long. Max length is %v", length)
|
||||
}
|
||||
} else {
|
||||
// otherwise we cannot convert the value into a string and cannot enforce length
|
||||
return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name())
|
||||
}
|
||||
|
||||
// the input is fine
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MinLength requires that the string is longer or equal in length to the specified value
|
||||
func MinLength(length int) Validator {
|
||||
// return a validator that checks the length of the string
|
||||
return func(val interface{}) error {
|
||||
if str, ok := val.(string); ok {
|
||||
// if the string is shorter than the given value
|
||||
if len([]rune(str)) < length {
|
||||
// yell loudly
|
||||
return fmt.Errorf("value is too short. Min length is %v", length)
|
||||
}
|
||||
} else {
|
||||
// otherwise we cannot convert the value into a string and cannot enforce length
|
||||
return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name())
|
||||
}
|
||||
|
||||
// the input is fine
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxItems requires that the list is no longer than the specified value
|
||||
func MaxItems(numberItems int) Validator {
|
||||
// return a validator that checks the length of the list
|
||||
return func(val interface{}) error {
|
||||
if list, ok := val.([]core.OptionAnswer); ok {
|
||||
// if the list is longer than the given value
|
||||
if len(list) > numberItems {
|
||||
// yell loudly
|
||||
return fmt.Errorf("value is too long. Max items is %v", numberItems)
|
||||
}
|
||||
} else {
|
||||
// otherwise we cannot convert the value into a list of answer and cannot enforce length
|
||||
return fmt.Errorf("cannot impose the length on something other than a list of answers")
|
||||
}
|
||||
// the input is fine
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MinItems requires that the list is longer or equal in length to the specified value
|
||||
func MinItems(numberItems int) Validator {
|
||||
// return a validator that checks the length of the list
|
||||
return func(val interface{}) error {
|
||||
if list, ok := val.([]core.OptionAnswer); ok {
|
||||
// if the list is shorter than the given value
|
||||
if len(list) < numberItems {
|
||||
// yell loudly
|
||||
return fmt.Errorf("value is too long. Min items is %v", numberItems)
|
||||
}
|
||||
} else {
|
||||
// otherwise we cannot convert the value into a list of answer and cannot enforce length
|
||||
return fmt.Errorf("cannot impose the length on something other than a list of answers")
|
||||
}
|
||||
// the input is fine
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ComposeValidators is a variadic function used to create one validator from many.
|
||||
func ComposeValidators(validators ...Validator) Validator {
|
||||
// return a validator that calls each one sequentially
|
||||
return func(val interface{}) error {
|
||||
// execute each validator
|
||||
for _, validator := range validators {
|
||||
// if the answer's value is not valid
|
||||
if err := validator(val); err != nil {
|
||||
// return the error
|
||||
return err
|
||||
}
|
||||
}
|
||||
// we passed all validators, the answer is valid
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// isZero returns true if the passed value is the zero object
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
return v.Len() == 0
|
||||
}
|
||||
|
||||
// compare the types directly with more general coverage
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
|
@ -1 +0,0 @@
|
|||
*.exe
|
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
1
vendor/github.com/Microsoft/go-winio/CODEOWNERS
generated
vendored
|
@ -1 +0,0 @@
|
|||
* @microsoft/containerplat
|
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
# go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
|
||||
|
||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||
for using named pipes as a net transport.
|
||||
|
||||
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||
package.
|
||||
|
||||
Please see the LICENSE file for licensing information.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||
see the [Code of Conduct
|
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||
questions or comments.
|
||||
|
||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||
for another named pipe implementation.
|
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
|
@ -1,280 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||
|
||||
const (
|
||||
BackupData = uint32(iota + 1)
|
||||
BackupEaData
|
||||
BackupSecurity
|
||||
BackupAlternateData
|
||||
BackupLink
|
||||
BackupPropertyData
|
||||
BackupObjectId
|
||||
BackupReparseData
|
||||
BackupSparseBlock
|
||||
BackupTxfsData
|
||||
)
|
||||
|
||||
const (
|
||||
StreamSparseAttributes = uint32(8)
|
||||
)
|
||||
|
||||
const (
|
||||
WRITE_DAC = 0x40000
|
||||
WRITE_OWNER = 0x80000
|
||||
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||
)
|
||||
|
||||
// BackupHeader represents a backup stream of a file.
|
||||
type BackupHeader struct {
|
||||
Id uint32 // The backup stream ID
|
||||
Attributes uint32 // Stream attributes
|
||||
Size int64 // The size of the stream in bytes
|
||||
Name string // The name of the stream (for BackupAlternateData only).
|
||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||
}
|
||||
|
||||
type win32StreamId struct {
|
||||
StreamId uint32
|
||||
Attributes uint32
|
||||
Size uint64
|
||||
NameSize uint32
|
||||
}
|
||||
|
||||
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||
// of BackupHeader values.
|
||||
type BackupStreamReader struct {
|
||||
r io.Reader
|
||||
bytesLeft int64
|
||||
}
|
||||
|
||||
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||
return &BackupStreamReader{r, 0}
|
||||
}
|
||||
|
||||
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||
// it was not completely read.
|
||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||
if r.bytesLeft > 0 {
|
||||
if s, ok := r.r.(io.Seeker); ok {
|
||||
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||
// before trying the actual seek.
|
||||
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
|
||||
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.bytesLeft = 0
|
||||
}
|
||||
}
|
||||
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var wsi win32StreamId
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr := &BackupHeader{
|
||||
Id: wsi.StreamId,
|
||||
Attributes: wsi.Attributes,
|
||||
Size: int64(wsi.Size),
|
||||
}
|
||||
if wsi.NameSize != 0 {
|
||||
name := make([]uint16, int(wsi.NameSize/2))
|
||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr.Name = syscall.UTF16ToString(name)
|
||||
}
|
||||
if wsi.StreamId == BackupSparseBlock {
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr.Size -= 8
|
||||
}
|
||||
r.bytesLeft = hdr.Size
|
||||
return hdr, nil
|
||||
}
|
||||
|
||||
// Read reads from the current backup stream.
|
||||
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||
if r.bytesLeft == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if int64(len(b)) > r.bytesLeft {
|
||||
b = b[:r.bytesLeft]
|
||||
}
|
||||
n, err := r.r.Read(b)
|
||||
r.bytesLeft -= int64(n)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
} else if r.bytesLeft == 0 && err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||
type BackupStreamWriter struct {
|
||||
w io.Writer
|
||||
bytesLeft int64
|
||||
}
|
||||
|
||||
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||
return &BackupStreamWriter{w, 0}
|
||||
}
|
||||
|
||||
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||
if w.bytesLeft != 0 {
|
||||
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||
}
|
||||
name := utf16.Encode([]rune(hdr.Name))
|
||||
wsi := win32StreamId{
|
||||
StreamId: hdr.Id,
|
||||
Attributes: hdr.Attributes,
|
||||
Size: uint64(hdr.Size),
|
||||
NameSize: uint32(len(name) * 2),
|
||||
}
|
||||
if hdr.Id == BackupSparseBlock {
|
||||
// Include space for the int64 block offset
|
||||
wsi.Size += 8
|
||||
}
|
||||
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(name) != 0 {
|
||||
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if hdr.Id == BackupSparseBlock {
|
||||
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.bytesLeft = hdr.Size
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes to the current backup stream.
|
||||
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||
if w.bytesLeft < int64(len(b)) {
|
||||
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||
}
|
||||
n, err := w.w.Write(b)
|
||||
w.bytesLeft -= int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||
type BackupFileReader struct {
|
||||
f *os.File
|
||||
includeSecurity bool
|
||||
ctx uintptr
|
||||
}
|
||||
|
||||
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||
// Read will attempt to read the security descriptor of the file.
|
||||
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||
r := &BackupFileReader{f, includeSecurity, 0}
|
||||
return r
|
||||
}
|
||||
|
||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||
var bytesRead uint32
|
||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||
}
|
||||
runtime.KeepAlive(r.f)
|
||||
if bytesRead == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(bytesRead), nil
|
||||
}
|
||||
|
||||
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||
// the underlying file.
|
||||
func (r *BackupFileReader) Close() error {
|
||||
if r.ctx != 0 {
|
||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||
runtime.KeepAlive(r.f)
|
||||
r.ctx = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||
type BackupFileWriter struct {
|
||||
f *os.File
|
||||
includeSecurity bool
|
||||
ctx uintptr
|
||||
}
|
||||
|
||||
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||
// Write() will attempt to restore the security descriptor from the stream.
|
||||
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||
return w
|
||||
}
|
||||
|
||||
// Write restores a portion of the file using the provided backup stream.
|
||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||
var bytesWritten uint32
|
||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||
}
|
||||
runtime.KeepAlive(w.f)
|
||||
if int(bytesWritten) != len(b) {
|
||||
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||
// close the underlying file.
|
||||
func (w *BackupFileWriter) Close() error {
|
||||
if w.ctx != 0 {
|
||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||
runtime.KeepAlive(w.f)
|
||||
w.ctx = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||
// or restore privileges have been acquired.
|
||||
//
|
||||
// If the file opened was a directory, it cannot be used with Readdir().
|
||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||
winPath, err := syscall.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(h), path), nil
|
||||
}
|
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
|
@ -1,137 +0,0 @@
|
|||
package winio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type fileFullEaInformation struct {
|
||||
NextEntryOffset uint32
|
||||
Flags uint8
|
||||
NameLength uint8
|
||||
ValueLength uint16
|
||||
}
|
||||
|
||||
var (
|
||||
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
||||
|
||||
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
||||
errEaNameTooLarge = errors.New("extended attribute name too large")
|
||||
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||
)
|
||||
|
||||
// ExtendedAttribute represents a single Windows EA.
|
||||
type ExtendedAttribute struct {
|
||||
Name string
|
||||
Value []byte
|
||||
Flags uint8
|
||||
}
|
||||
|
||||
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||
var info fileFullEaInformation
|
||||
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||
if err != nil {
|
||||
err = errInvalidEaBuffer
|
||||
return
|
||||
}
|
||||
|
||||
nameOffset := fileFullEaInformationSize
|
||||
nameLen := int(info.NameLength)
|
||||
valueOffset := nameOffset + int(info.NameLength) + 1
|
||||
valueLen := int(info.ValueLength)
|
||||
nextOffset := int(info.NextEntryOffset)
|
||||
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||
err = errInvalidEaBuffer
|
||||
return
|
||||
}
|
||||
|
||||
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||
ea.Value = b[valueOffset : valueOffset+valueLen]
|
||||
ea.Flags = info.Flags
|
||||
if info.NextEntryOffset != 0 {
|
||||
nb = b[info.NextEntryOffset:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||
for len(b) != 0 {
|
||||
ea, nb, err := parseEa(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eas = append(eas, ea)
|
||||
b = nb
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||
return errEaNameTooLarge
|
||||
}
|
||||
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
||||
return errEaValueTooLarge
|
||||
}
|
||||
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
||||
withPadding := (entrySize + 3) &^ 3
|
||||
nextOffset := uint32(0)
|
||||
if !last {
|
||||
nextOffset = withPadding
|
||||
}
|
||||
info := fileFullEaInformation{
|
||||
NextEntryOffset: nextOffset,
|
||||
Flags: ea.Flags,
|
||||
NameLength: uint8(len(ea.Name)),
|
||||
ValueLength: uint16(len(ea.Value)),
|
||||
}
|
||||
|
||||
err := binary.Write(buf, binary.LittleEndian, &info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = buf.Write([]byte(ea.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = buf.WriteByte(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = buf.Write(ea.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for i := range eas {
|
||||
last := false
|
||||
if i == len(eas)-1 {
|
||||
last = true
|
||||
}
|
||||
|
||||
err := writeEa(&buf, &eas[i], last)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
323
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
323
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
|
@ -1,323 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
|
||||
|
||||
type atomicBool int32
|
||||
|
||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||
func (b *atomicBool) swap(new bool) bool {
|
||||
var newInt int32
|
||||
if new {
|
||||
newInt = 1
|
||||
}
|
||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
||||
}
|
||||
|
||||
const (
|
||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFileClosed = errors.New("file has already been closed")
|
||||
ErrTimeout = &timeoutError{}
|
||||
)
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *timeoutError) Timeout() bool { return true }
|
||||
func (e *timeoutError) Temporary() bool { return true }
|
||||
|
||||
type timeoutChan chan struct{}
|
||||
|
||||
var ioInitOnce sync.Once
|
||||
var ioCompletionPort syscall.Handle
|
||||
|
||||
// ioResult contains the result of an asynchronous IO operation
|
||||
type ioResult struct {
|
||||
bytes uint32
|
||||
err error
|
||||
}
|
||||
|
||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||
type ioOperation struct {
|
||||
o syscall.Overlapped
|
||||
ch chan ioResult
|
||||
}
|
||||
|
||||
func initIo() {
|
||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ioCompletionPort = h
|
||||
go ioCompletionProcessor(h)
|
||||
}
|
||||
|
||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||
type win32File struct {
|
||||
handle syscall.Handle
|
||||
wg sync.WaitGroup
|
||||
wgLock sync.RWMutex
|
||||
closing atomicBool
|
||||
socket bool
|
||||
readDeadline deadlineHandler
|
||||
writeDeadline deadlineHandler
|
||||
}
|
||||
|
||||
type deadlineHandler struct {
|
||||
setLock sync.Mutex
|
||||
channel timeoutChan
|
||||
channelLock sync.RWMutex
|
||||
timer *time.Timer
|
||||
timedout atomicBool
|
||||
}
|
||||
|
||||
// makeWin32File makes a new win32File from an existing file handle
|
||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||
f := &win32File{handle: h}
|
||||
ioInitOnce.Do(initIo)
|
||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.readDeadline.channel = make(timeoutChan)
|
||||
f.writeDeadline.channel = make(timeoutChan)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||
// If we return the result of makeWin32File directly, it can result in an
|
||||
// interface-wrapped nil, rather than a nil interface value.
|
||||
f, err := makeWin32File(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// closeHandle closes the resources associated with a Win32 handle
|
||||
func (f *win32File) closeHandle() {
|
||||
f.wgLock.Lock()
|
||||
// Atomically set that we are closing, releasing the resources only once.
|
||||
if !f.closing.swap(true) {
|
||||
f.wgLock.Unlock()
|
||||
// cancel all IO and wait for it to complete
|
||||
cancelIoEx(f.handle, nil)
|
||||
f.wg.Wait()
|
||||
// at this point, no new IO can start
|
||||
syscall.Close(f.handle)
|
||||
f.handle = 0
|
||||
} else {
|
||||
f.wgLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes a win32File.
|
||||
func (f *win32File) Close() error {
|
||||
f.closeHandle()
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareIo prepares for a new IO operation.
|
||||
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||
f.wgLock.RLock()
|
||||
if f.closing.isSet() {
|
||||
f.wgLock.RUnlock()
|
||||
return nil, ErrFileClosed
|
||||
}
|
||||
f.wg.Add(1)
|
||||
f.wgLock.RUnlock()
|
||||
c := &ioOperation{}
|
||||
c.ch = make(chan ioResult)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ioCompletionProcessor processes completed async IOs forever
|
||||
func ioCompletionProcessor(h syscall.Handle) {
|
||||
for {
|
||||
var bytes uint32
|
||||
var key uintptr
|
||||
var op *ioOperation
|
||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||
if op == nil {
|
||||
panic(err)
|
||||
}
|
||||
op.ch <- ioResult{bytes, err}
|
||||
}
|
||||
}
|
||||
|
||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||
// the operation has actually completed.
|
||||
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||
if err != syscall.ERROR_IO_PENDING {
|
||||
return int(bytes), err
|
||||
}
|
||||
|
||||
if f.closing.isSet() {
|
||||
cancelIoEx(f.handle, &c.o)
|
||||
}
|
||||
|
||||
var timeout timeoutChan
|
||||
if d != nil {
|
||||
d.channelLock.Lock()
|
||||
timeout = d.channel
|
||||
d.channelLock.Unlock()
|
||||
}
|
||||
|
||||
var r ioResult
|
||||
select {
|
||||
case r = <-c.ch:
|
||||
err = r.err
|
||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||
if f.closing.isSet() {
|
||||
err = ErrFileClosed
|
||||
}
|
||||
} else if err != nil && f.socket {
|
||||
// err is from Win32. Query the overlapped structure to get the winsock error.
|
||||
var bytes, flags uint32
|
||||
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
|
||||
}
|
||||
case <-timeout:
|
||||
cancelIoEx(f.handle, &c.o)
|
||||
r = <-c.ch
|
||||
err = r.err
|
||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||
err = ErrTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// runtime.KeepAlive is needed, as c is passed via native
|
||||
// code to ioCompletionProcessor, c must remain alive
|
||||
// until the channel read is complete.
|
||||
runtime.KeepAlive(c)
|
||||
return int(r.bytes), err
|
||||
}
|
||||
|
||||
// Read reads from a file handle.
|
||||
func (f *win32File) Read(b []byte) (int, error) {
|
||||
c, err := f.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.wg.Done()
|
||||
|
||||
if f.readDeadline.timedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
}
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
|
||||
runtime.KeepAlive(b)
|
||||
|
||||
// Handle EOF conditions.
|
||||
if err == nil && n == 0 && len(b) != 0 {
|
||||
return 0, io.EOF
|
||||
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||
return 0, io.EOF
|
||||
} else {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes to a file handle.
|
||||
func (f *win32File) Write(b []byte) (int, error) {
|
||||
c, err := f.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.wg.Done()
|
||||
|
||||
if f.writeDeadline.timedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
}
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
|
||||
runtime.KeepAlive(b)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *win32File) SetReadDeadline(deadline time.Time) error {
|
||||
return f.readDeadline.set(deadline)
|
||||
}
|
||||
|
||||
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||
return f.writeDeadline.set(deadline)
|
||||
}
|
||||
|
||||
func (f *win32File) Flush() error {
|
||||
return syscall.FlushFileBuffers(f.handle)
|
||||
}
|
||||
|
||||
func (f *win32File) Fd() uintptr {
|
||||
return uintptr(f.handle)
|
||||
}
|
||||
|
||||
func (d *deadlineHandler) set(deadline time.Time) error {
|
||||
d.setLock.Lock()
|
||||
defer d.setLock.Unlock()
|
||||
|
||||
if d.timer != nil {
|
||||
if !d.timer.Stop() {
|
||||
<-d.channel
|
||||
}
|
||||
d.timer = nil
|
||||
}
|
||||
d.timedout.setFalse()
|
||||
|
||||
select {
|
||||
case <-d.channel:
|
||||
d.channelLock.Lock()
|
||||
d.channel = make(chan struct{})
|
||||
d.channelLock.Unlock()
|
||||
default:
|
||||
}
|
||||
|
||||
if deadline.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
timeoutIO := func() {
|
||||
d.timedout.setTrue()
|
||||
close(d.channel)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
duration := deadline.Sub(now)
|
||||
if deadline.After(now) {
|
||||
// Deadline is in the future, set a timer to wait
|
||||
d.timer = time.AfterFunc(duration, timeoutIO)
|
||||
} else {
|
||||
// Deadline is in the past. Cancel all pending IO now.
|
||||
timeoutIO()
|
||||
}
|
||||
return nil
|
||||
}
|
73
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
73
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
|
@ -1,73 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// FileBasicInfo contains file access time and file attributes information.
|
||||
type FileBasicInfo struct {
|
||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
|
||||
FileAttributes uint32
|
||||
pad uint32 // padding
|
||||
}
|
||||
|
||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||
bi := &FileBasicInfo{}
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// SetFileBasicInfo sets times and attributes for a file.
|
||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||
if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileStandardInfo contains extended information for the file.
|
||||
// FILE_STANDARD_INFO in WinBase.h
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
|
||||
type FileStandardInfo struct {
|
||||
AllocationSize, EndOfFile int64
|
||||
NumberOfLinks uint32
|
||||
DeletePending, Directory bool
|
||||
}
|
||||
|
||||
// GetFileStandardInfo retrieves ended information for the file.
|
||||
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
|
||||
si := &FileStandardInfo{}
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return si, nil
|
||||
}
|
||||
|
||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||
// unique on a system.
|
||||
type FileIDInfo struct {
|
||||
VolumeSerialNumber uint64
|
||||
FileID [16]byte
|
||||
}
|
||||
|
||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||
fileID := &FileIDInfo{}
|
||||
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(f)
|
||||
return fileID, nil
|
||||
}
|
9
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
9
vendor/github.com/Microsoft/go-winio/go.mod
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
module github.com/Microsoft/go-winio
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||
)
|
14
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
14
vendor/github.com/Microsoft/go-winio/go.sum
generated
vendored
|
@ -1,14 +0,0 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
307
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
307
vendor/github.com/Microsoft/go-winio/hvsock.go
generated
vendored
|
@ -1,307 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
)
|
||||
|
||||
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
|
||||
|
||||
const (
|
||||
afHvSock = 34 // AF_HYPERV
|
||||
|
||||
socketError = ^uintptr(0)
|
||||
)
|
||||
|
||||
// An HvsockAddr is an address for a AF_HYPERV socket.
|
||||
type HvsockAddr struct {
|
||||
VMID guid.GUID
|
||||
ServiceID guid.GUID
|
||||
}
|
||||
|
||||
type rawHvsockAddr struct {
|
||||
Family uint16
|
||||
_ uint16
|
||||
VMID guid.GUID
|
||||
ServiceID guid.GUID
|
||||
}
|
||||
|
||||
// Network returns the address's network name, "hvsock".
|
||||
func (addr *HvsockAddr) Network() string {
|
||||
return "hvsock"
|
||||
}
|
||||
|
||||
func (addr *HvsockAddr) String() string {
|
||||
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
|
||||
}
|
||||
|
||||
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
|
||||
func VsockServiceID(port uint32) guid.GUID {
|
||||
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
|
||||
g.Data1 = port
|
||||
return g
|
||||
}
|
||||
|
||||
func (addr *HvsockAddr) raw() rawHvsockAddr {
|
||||
return rawHvsockAddr{
|
||||
Family: afHvSock,
|
||||
VMID: addr.VMID,
|
||||
ServiceID: addr.ServiceID,
|
||||
}
|
||||
}
|
||||
|
||||
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
|
||||
addr.VMID = raw.VMID
|
||||
addr.ServiceID = raw.ServiceID
|
||||
}
|
||||
|
||||
// HvsockListener is a socket listener for the AF_HYPERV address family.
|
||||
type HvsockListener struct {
|
||||
sock *win32File
|
||||
addr HvsockAddr
|
||||
}
|
||||
|
||||
// HvsockConn is a connected socket of the AF_HYPERV address family.
|
||||
type HvsockConn struct {
|
||||
sock *win32File
|
||||
local, remote HvsockAddr
|
||||
}
|
||||
|
||||
func newHvSocket() (*win32File, error) {
|
||||
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
f, err := makeWin32File(fd)
|
||||
if err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
f.socket = true
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// ListenHvsock listens for connections on the specified hvsock address.
|
||||
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
|
||||
l := &HvsockListener{addr: *addr}
|
||||
sock, err := newHvSocket()
|
||||
if err != nil {
|
||||
return nil, l.opErr("listen", err)
|
||||
}
|
||||
sa := addr.raw()
|
||||
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
|
||||
if err != nil {
|
||||
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
|
||||
}
|
||||
err = syscall.Listen(sock.handle, 16)
|
||||
if err != nil {
|
||||
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
|
||||
}
|
||||
return &HvsockListener{sock: sock, addr: *addr}, nil
|
||||
}
|
||||
|
||||
func (l *HvsockListener) opErr(op string, err error) error {
|
||||
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *HvsockListener) Addr() net.Addr {
|
||||
return &l.addr
|
||||
}
|
||||
|
||||
// Accept waits for the next connection and returns it.
|
||||
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||
sock, err := newHvSocket()
|
||||
if err != nil {
|
||||
return nil, l.opErr("accept", err)
|
||||
}
|
||||
defer func() {
|
||||
if sock != nil {
|
||||
sock.Close()
|
||||
}
|
||||
}()
|
||||
c, err := l.sock.prepareIo()
|
||||
if err != nil {
|
||||
return nil, l.opErr("accept", err)
|
||||
}
|
||||
defer l.sock.wg.Done()
|
||||
|
||||
// AcceptEx, per documentation, requires an extra 16 bytes per address.
|
||||
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
|
||||
var addrbuf [addrlen * 2]byte
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
|
||||
_, err = l.sock.asyncIo(c, nil, bytes, err)
|
||||
if err != nil {
|
||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||
}
|
||||
conn := &HvsockConn{
|
||||
sock: sock,
|
||||
}
|
||||
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
|
||||
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
|
||||
sock = nil
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Close closes the listener, causing any pending Accept calls to fail.
|
||||
func (l *HvsockListener) Close() error {
|
||||
return l.sock.Close()
|
||||
}
|
||||
|
||||
/* Need to finish ConnectEx handling
|
||||
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
|
||||
sock, err := newHvSocket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if sock != nil {
|
||||
sock.Close()
|
||||
}
|
||||
}()
|
||||
c, err := sock.prepareIo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer sock.wg.Done()
|
||||
var bytes uint32
|
||||
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
|
||||
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn := &HvsockConn{
|
||||
sock: sock,
|
||||
remote: *addr,
|
||||
}
|
||||
sock = nil
|
||||
return conn, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (conn *HvsockConn) opErr(op string, err error) error {
|
||||
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
|
||||
}
|
||||
|
||||
func (conn *HvsockConn) Read(b []byte) (int, error) {
|
||||
c, err := conn.sock.prepareIo()
|
||||
if err != nil {
|
||||
return 0, conn.opErr("read", err)
|
||||
}
|
||||
defer conn.sock.wg.Done()
|
||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||
var flags, bytes uint32
|
||||
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
|
||||
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
|
||||
if err != nil {
|
||||
if _, ok := err.(syscall.Errno); ok {
|
||||
err = os.NewSyscallError("wsarecv", err)
|
||||
}
|
||||
return 0, conn.opErr("read", err)
|
||||
} else if n == 0 {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (conn *HvsockConn) Write(b []byte) (int, error) {
|
||||
t := 0
|
||||
for len(b) != 0 {
|
||||
n, err := conn.write(b)
|
||||
if err != nil {
|
||||
return t + n, err
|
||||
}
|
||||
t += n
|
||||
b = b[n:]
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (conn *HvsockConn) write(b []byte) (int, error) {
|
||||
c, err := conn.sock.prepareIo()
|
||||
if err != nil {
|
||||
return 0, conn.opErr("write", err)
|
||||
}
|
||||
defer conn.sock.wg.Done()
|
||||
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
|
||||
var bytes uint32
|
||||
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
|
||||
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
|
||||
if err != nil {
|
||||
if _, ok := err.(syscall.Errno); ok {
|
||||
err = os.NewSyscallError("wsasend", err)
|
||||
}
|
||||
return 0, conn.opErr("write", err)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close closes the socket connection, failing any pending read or write calls.
|
||||
func (conn *HvsockConn) Close() error {
|
||||
return conn.sock.Close()
|
||||
}
|
||||
|
||||
func (conn *HvsockConn) shutdown(how int) error {
|
||||
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
|
||||
if err != nil {
|
||||
return os.NewSyscallError("shutdown", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseRead shuts down the read end of the socket.
|
||||
func (conn *HvsockConn) CloseRead() error {
|
||||
err := conn.shutdown(syscall.SHUT_RD)
|
||||
if err != nil {
|
||||
return conn.opErr("close", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
|
||||
// no more data will be written.
|
||||
func (conn *HvsockConn) CloseWrite() error {
|
||||
err := conn.shutdown(syscall.SHUT_WR)
|
||||
if err != nil {
|
||||
return conn.opErr("close", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of the connection.
|
||||
func (conn *HvsockConn) LocalAddr() net.Addr {
|
||||
return &conn.local
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of the connection.
|
||||
func (conn *HvsockConn) RemoteAddr() net.Addr {
|
||||
return &conn.remote
|
||||
}
|
||||
|
||||
// SetDeadline implements the net.Conn SetDeadline method.
|
||||
func (conn *HvsockConn) SetDeadline(t time.Time) error {
|
||||
conn.SetReadDeadline(t)
|
||||
conn.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
|
||||
return conn.sock.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
|
||||
return conn.sock.SetWriteDeadline(t)
|
||||
}
|
517
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
517
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
|
@ -1,517 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
||||
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
|
||||
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
|
||||
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
|
||||
|
||||
type ioStatusBlock struct {
|
||||
Status, Information uintptr
|
||||
}
|
||||
|
||||
type objectAttributes struct {
|
||||
Length uintptr
|
||||
RootDirectory uintptr
|
||||
ObjectName *unicodeString
|
||||
Attributes uintptr
|
||||
SecurityDescriptor *securityDescriptor
|
||||
SecurityQoS uintptr
|
||||
}
|
||||
|
||||
type unicodeString struct {
|
||||
Length uint16
|
||||
MaximumLength uint16
|
||||
Buffer uintptr
|
||||
}
|
||||
|
||||
type securityDescriptor struct {
|
||||
Revision byte
|
||||
Sbz1 byte
|
||||
Control uint16
|
||||
Owner uintptr
|
||||
Group uintptr
|
||||
Sacl uintptr
|
||||
Dacl uintptr
|
||||
}
|
||||
|
||||
type ntstatus int32
|
||||
|
||||
func (status ntstatus) Err() error {
|
||||
if status >= 0 {
|
||||
return nil
|
||||
}
|
||||
return rtlNtStatusToDosError(status)
|
||||
}
|
||||
|
||||
const (
|
||||
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||
cERROR_NO_DATA = syscall.Errno(232)
|
||||
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||
|
||||
cSECURITY_SQOS_PRESENT = 0x100000
|
||||
cSECURITY_ANONYMOUS = 0
|
||||
|
||||
cPIPE_TYPE_MESSAGE = 4
|
||||
|
||||
cPIPE_READMODE_MESSAGE = 2
|
||||
|
||||
cFILE_OPEN = 1
|
||||
cFILE_CREATE = 2
|
||||
|
||||
cFILE_PIPE_MESSAGE_TYPE = 1
|
||||
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
|
||||
|
||||
cSE_DACL_PRESENT = 4
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
||||
|
||||
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||
)
|
||||
|
||||
type win32Pipe struct {
|
||||
*win32File
|
||||
path string
|
||||
}
|
||||
|
||||
type win32MessageBytePipe struct {
|
||||
win32Pipe
|
||||
writeClosed bool
|
||||
readEOF bool
|
||||
}
|
||||
|
||||
type pipeAddress string
|
||||
|
||||
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||
return pipeAddress(f.path)
|
||||
}
|
||||
|
||||
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||
return pipeAddress(f.path)
|
||||
}
|
||||
|
||||
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||
f.SetReadDeadline(t)
|
||||
f.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||
if f.writeClosed {
|
||||
return errPipeWriteClosed
|
||||
}
|
||||
err := f.win32File.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.win32File.Write(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.writeClosed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||
// they are used to implement CloseWrite().
|
||||
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
||||
if f.writeClosed {
|
||||
return 0, errPipeWriteClosed
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return f.win32File.Write(b)
|
||||
}
|
||||
|
||||
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||
if f.readEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err := f.win32File.Read(b)
|
||||
if err == io.EOF {
|
||||
// If this was the result of a zero-byte read, then
|
||||
// it is possible that the read was due to a zero-size
|
||||
// message. Since we are simulating CloseWrite with a
|
||||
// zero-byte message, ensure that all future Read() calls
|
||||
// also return EOF.
|
||||
f.readEOF = true
|
||||
} else if err == syscall.ERROR_MORE_DATA {
|
||||
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||
// and the message still has more bytes. Treat this as a success, since
|
||||
// this package presents all named pipes as byte streams.
|
||||
err = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s pipeAddress) Network() string {
|
||||
return "pipe"
|
||||
}
|
||||
|
||||
func (s pipeAddress) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
|
||||
for {
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return syscall.Handle(0), ctx.Err()
|
||||
default:
|
||||
h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||
if err == nil {
|
||||
return h, nil
|
||||
}
|
||||
if err != cERROR_PIPE_BUSY {
|
||||
return h, &os.PathError{Err: err, Op: "open", Path: *path}
|
||||
}
|
||||
// Wait 10 msec and try again. This is a rather simplistic
|
||||
// view, as we always try each 10 milliseconds.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||
// takes longer than the specified duration. If timeout is nil, then we use
|
||||
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
|
||||
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||
var absTimeout time.Time
|
||||
if timeout != nil {
|
||||
absTimeout = time.Now().Add(*timeout)
|
||||
} else {
|
||||
absTimeout = time.Now().Add(2 * time.Second)
|
||||
}
|
||||
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
|
||||
conn, err := DialPipeContext(ctx, path)
|
||||
if err == context.DeadlineExceeded {
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
|
||||
// cancellation or timeout.
|
||||
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
|
||||
}
|
||||
|
||||
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
|
||||
// cancellation or timeout.
|
||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||
var err error
|
||||
var h syscall.Handle
|
||||
h, err = tryDialPipe(ctx, &path, access)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var flags uint32
|
||||
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err := makeWin32File(h)
|
||||
if err != nil {
|
||||
syscall.Close(h)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the pipe is in message mode, return a message byte pipe, which
|
||||
// supports CloseWrite().
|
||||
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
||||
return &win32MessageBytePipe{
|
||||
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||
}, nil
|
||||
}
|
||||
return &win32Pipe{win32File: f, path: path}, nil
|
||||
}
|
||||
|
||||
type acceptResponse struct {
|
||||
f *win32File
|
||||
err error
|
||||
}
|
||||
|
||||
type win32PipeListener struct {
|
||||
firstHandle syscall.Handle
|
||||
path string
|
||||
config PipeConfig
|
||||
acceptCh chan (chan acceptResponse)
|
||||
closeCh chan int
|
||||
doneCh chan int
|
||||
}
|
||||
|
||||
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
||||
path16, err := syscall.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
|
||||
var oa objectAttributes
|
||||
oa.Length = unsafe.Sizeof(oa)
|
||||
|
||||
var ntPath unicodeString
|
||||
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
|
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
defer localFree(ntPath.Buffer)
|
||||
oa.ObjectName = &ntPath
|
||||
|
||||
// The security descriptor is only needed for the first pipe.
|
||||
if first {
|
||||
if sd != nil {
|
||||
len := uint32(len(sd))
|
||||
sdb := localAlloc(0, len)
|
||||
defer localFree(sdb)
|
||||
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
|
||||
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
|
||||
} else {
|
||||
// Construct the default named pipe security descriptor.
|
||||
var dacl uintptr
|
||||
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
|
||||
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
|
||||
}
|
||||
defer localFree(dacl)
|
||||
|
||||
sdb := &securityDescriptor{
|
||||
Revision: 1,
|
||||
Control: cSE_DACL_PRESENT,
|
||||
Dacl: dacl,
|
||||
}
|
||||
oa.SecurityDescriptor = sdb
|
||||
}
|
||||
}
|
||||
|
||||
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
|
||||
if c.MessageMode {
|
||||
typ |= cFILE_PIPE_MESSAGE_TYPE
|
||||
}
|
||||
|
||||
disposition := uint32(cFILE_OPEN)
|
||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
|
||||
if first {
|
||||
disposition = cFILE_CREATE
|
||||
// By not asking for read or write access, the named pipe file system
|
||||
// will put this pipe into an initially disconnected state, blocking
|
||||
// client connections until the next call with first == false.
|
||||
access = syscall.SYNCHRONIZE
|
||||
}
|
||||
|
||||
timeout := int64(-50 * 10000) // 50ms
|
||||
|
||||
var (
|
||||
h syscall.Handle
|
||||
iosb ioStatusBlock
|
||||
)
|
||||
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
|
||||
if err != nil {
|
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
|
||||
runtime.KeepAlive(ntPath)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := makeWin32File(h)
|
||||
if err != nil {
|
||||
syscall.Close(h)
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
||||
p, err := l.makeServerPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wait for the client to connect.
|
||||
ch := make(chan error)
|
||||
go func(p *win32File) {
|
||||
ch <- connectPipe(p)
|
||||
}(p)
|
||||
|
||||
select {
|
||||
case err = <-ch:
|
||||
if err != nil {
|
||||
p.Close()
|
||||
p = nil
|
||||
}
|
||||
case <-l.closeCh:
|
||||
// Abort the connect request by closing the handle.
|
||||
p.Close()
|
||||
p = nil
|
||||
err = <-ch
|
||||
if err == nil || err == ErrFileClosed {
|
||||
err = ErrPipeListenerClosed
|
||||
}
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) listenerRoutine() {
|
||||
closed := false
|
||||
for !closed {
|
||||
select {
|
||||
case <-l.closeCh:
|
||||
closed = true
|
||||
case responseCh := <-l.acceptCh:
|
||||
var (
|
||||
p *win32File
|
||||
err error
|
||||
)
|
||||
for {
|
||||
p, err = l.makeConnectedServerPipe()
|
||||
// If the connection was immediately closed by the client, try
|
||||
// again.
|
||||
if err != cERROR_NO_DATA {
|
||||
break
|
||||
}
|
||||
}
|
||||
responseCh <- acceptResponse{p, err}
|
||||
closed = err == ErrPipeListenerClosed
|
||||
}
|
||||
}
|
||||
syscall.Close(l.firstHandle)
|
||||
l.firstHandle = 0
|
||||
// Notify Close() and Accept() callers that the handle has been closed.
|
||||
close(l.doneCh)
|
||||
}
|
||||
|
||||
// PipeConfig contain configuration for the pipe listener.
|
||||
type PipeConfig struct {
|
||||
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||
SecurityDescriptor string
|
||||
|
||||
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||
// case the pipe is read in byte mode by default. The only practical difference in
|
||||
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||
// when the pipe is in message mode.
|
||||
MessageMode bool
|
||||
|
||||
// InputBufferSize specifies the size of the input buffer, in bytes.
|
||||
InputBufferSize int32
|
||||
|
||||
// OutputBufferSize specifies the size of the output buffer, in bytes.
|
||||
OutputBufferSize int32
|
||||
}
|
||||
|
||||
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||
// The pipe must not already exist.
|
||||
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||
var (
|
||||
sd []byte
|
||||
err error
|
||||
)
|
||||
if c == nil {
|
||||
c = &PipeConfig{}
|
||||
}
|
||||
if c.SecurityDescriptor != "" {
|
||||
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
h, err := makeServerPipeHandle(path, sd, c, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := &win32PipeListener{
|
||||
firstHandle: h,
|
||||
path: path,
|
||||
config: *c,
|
||||
acceptCh: make(chan (chan acceptResponse)),
|
||||
closeCh: make(chan int),
|
||||
doneCh: make(chan int),
|
||||
}
|
||||
go l.listenerRoutine()
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func connectPipe(p *win32File) error {
|
||||
c, err := p.prepareIo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.wg.Done()
|
||||
|
||||
err = connectNamedPipe(p.handle, &c.o)
|
||||
_, err = p.asyncIo(c, nil, 0, err)
|
||||
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||
ch := make(chan acceptResponse)
|
||||
select {
|
||||
case l.acceptCh <- ch:
|
||||
response := <-ch
|
||||
err := response.err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.config.MessageMode {
|
||||
return &win32MessageBytePipe{
|
||||
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
||||
}, nil
|
||||
}
|
||||
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
||||
case <-l.doneCh:
|
||||
return nil, ErrPipeListenerClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Close() error {
|
||||
select {
|
||||
case l.closeCh <- 1:
|
||||
<-l.doneCh
|
||||
case <-l.doneCh:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Addr() net.Addr {
|
||||
return pipeAddress(l.path)
|
||||
}
|
237
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
237
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
generated
vendored
|
@ -1,237 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
// Package guid provides a GUID type. The backing structure for a GUID is
|
||||
// identical to that used by the golang.org/x/sys/windows GUID type.
|
||||
// There are two main binary encodings used for a GUID, the big-endian encoding,
|
||||
// and the Windows (mixed-endian) encoding. See here for details:
|
||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
|
||||
package guid
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Variant specifies which GUID variant (or "type") of the GUID. It determines
|
||||
// how the entirety of the rest of the GUID is interpreted.
|
||||
type Variant uint8
|
||||
|
||||
// The variants specified by RFC 4122.
|
||||
const (
|
||||
// VariantUnknown specifies a GUID variant which does not conform to one of
|
||||
// the variant encodings specified in RFC 4122.
|
||||
VariantUnknown Variant = iota
|
||||
VariantNCS
|
||||
VariantRFC4122
|
||||
VariantMicrosoft
|
||||
VariantFuture
|
||||
)
|
||||
|
||||
// Version specifies how the bits in the GUID were generated. For instance, a
|
||||
// version 4 GUID is randomly generated, and a version 5 is generated from the
|
||||
// hash of an input string.
|
||||
type Version uint8
|
||||
|
||||
var _ = (encoding.TextMarshaler)(GUID{})
|
||||
var _ = (encoding.TextUnmarshaler)(&GUID{})
|
||||
|
||||
// GUID represents a GUID/UUID. It has the same structure as
|
||||
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
|
||||
// that type. It is defined as its own type so that stringification and
|
||||
// marshaling can be supported. The representation matches that used by native
|
||||
// Windows code.
|
||||
type GUID windows.GUID
|
||||
|
||||
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
|
||||
func NewV4() (GUID, error) {
|
||||
var b [16]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return GUID{}, err
|
||||
}
|
||||
|
||||
g := FromArray(b)
|
||||
g.setVersion(4) // Version 4 means randomly generated.
|
||||
g.setVariant(VariantRFC4122)
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
|
||||
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
|
||||
// and the sample code treats it as a series of bytes, so we do the same here.
|
||||
//
|
||||
// Some implementations, such as those found on Windows, treat the name as a
|
||||
// big-endian UTF16 stream of bytes. If that is desired, the string can be
|
||||
// encoded as such before being passed to this function.
|
||||
func NewV5(namespace GUID, name []byte) (GUID, error) {
|
||||
b := sha1.New()
|
||||
namespaceBytes := namespace.ToArray()
|
||||
b.Write(namespaceBytes[:])
|
||||
b.Write(name)
|
||||
|
||||
a := [16]byte{}
|
||||
copy(a[:], b.Sum(nil))
|
||||
|
||||
g := FromArray(a)
|
||||
g.setVersion(5) // Version 5 means generated from a string.
|
||||
g.setVariant(VariantRFC4122)
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
|
||||
var g GUID
|
||||
g.Data1 = order.Uint32(b[0:4])
|
||||
g.Data2 = order.Uint16(b[4:6])
|
||||
g.Data3 = order.Uint16(b[6:8])
|
||||
copy(g.Data4[:], b[8:16])
|
||||
return g
|
||||
}
|
||||
|
||||
func (g GUID) toArray(order binary.ByteOrder) [16]byte {
|
||||
b := [16]byte{}
|
||||
order.PutUint32(b[0:4], g.Data1)
|
||||
order.PutUint16(b[4:6], g.Data2)
|
||||
order.PutUint16(b[6:8], g.Data3)
|
||||
copy(b[8:16], g.Data4[:])
|
||||
return b
|
||||
}
|
||||
|
||||
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
|
||||
func FromArray(b [16]byte) GUID {
|
||||
return fromArray(b, binary.BigEndian)
|
||||
}
|
||||
|
||||
// ToArray returns an array of 16 bytes representing the GUID in big-endian
|
||||
// encoding.
|
||||
func (g GUID) ToArray() [16]byte {
|
||||
return g.toArray(binary.BigEndian)
|
||||
}
|
||||
|
||||
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
|
||||
func FromWindowsArray(b [16]byte) GUID {
|
||||
return fromArray(b, binary.LittleEndian)
|
||||
}
|
||||
|
||||
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
|
||||
// encoding.
|
||||
func (g GUID) ToWindowsArray() [16]byte {
|
||||
return g.toArray(binary.LittleEndian)
|
||||
}
|
||||
|
||||
func (g GUID) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%08x-%04x-%04x-%04x-%012x",
|
||||
g.Data1,
|
||||
g.Data2,
|
||||
g.Data3,
|
||||
g.Data4[:2],
|
||||
g.Data4[2:])
|
||||
}
|
||||
|
||||
// FromString parses a string containing a GUID and returns the GUID. The only
|
||||
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
// format.
|
||||
func FromString(s string) (GUID, error) {
|
||||
if len(s) != 36 {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
var g GUID
|
||||
|
||||
data1, err := strconv.ParseUint(s[0:8], 16, 32)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
g.Data1 = uint32(data1)
|
||||
|
||||
data2, err := strconv.ParseUint(s[9:13], 16, 16)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
g.Data2 = uint16(data2)
|
||||
|
||||
data3, err := strconv.ParseUint(s[14:18], 16, 16)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
g.Data3 = uint16(data3)
|
||||
|
||||
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
|
||||
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
g.Data4[i] = uint8(v)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g *GUID) setVariant(v Variant) {
|
||||
d := g.Data4[0]
|
||||
switch v {
|
||||
case VariantNCS:
|
||||
d = (d & 0x7f)
|
||||
case VariantRFC4122:
|
||||
d = (d & 0x3f) | 0x80
|
||||
case VariantMicrosoft:
|
||||
d = (d & 0x1f) | 0xc0
|
||||
case VariantFuture:
|
||||
d = (d & 0x0f) | 0xe0
|
||||
case VariantUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid variant: %d", v))
|
||||
}
|
||||
g.Data4[0] = d
|
||||
}
|
||||
|
||||
// Variant returns the GUID variant, as defined in RFC 4122.
|
||||
func (g GUID) Variant() Variant {
|
||||
b := g.Data4[0]
|
||||
if b&0x80 == 0 {
|
||||
return VariantNCS
|
||||
} else if b&0xc0 == 0x80 {
|
||||
return VariantRFC4122
|
||||
} else if b&0xe0 == 0xc0 {
|
||||
return VariantMicrosoft
|
||||
} else if b&0xe0 == 0xe0 {
|
||||
return VariantFuture
|
||||
}
|
||||
return VariantUnknown
|
||||
}
|
||||
|
||||
func (g *GUID) setVersion(v Version) {
|
||||
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
|
||||
}
|
||||
|
||||
// Version returns the GUID version, as defined in RFC 4122.
|
||||
func (g GUID) Version() Version {
|
||||
return Version((g.Data3 & 0xF000) >> 12)
|
||||
}
|
||||
|
||||
// MarshalText returns the textual representation of the GUID.
|
||||
func (g GUID) MarshalText() ([]byte, error) {
|
||||
return []byte(g.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
|
||||
// into this GUID.
|
||||
func (g *GUID) UnmarshalText(text []byte) error {
|
||||
g2, err := FromString(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*g = g2
|
||||
return nil
|
||||
}
|
203
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
203
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
|
@ -1,203 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||
|
||||
const (
|
||||
SE_PRIVILEGE_ENABLED = 2
|
||||
|
||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||
|
||||
SeBackupPrivilege = "SeBackupPrivilege"
|
||||
SeRestorePrivilege = "SeRestorePrivilege"
|
||||
SeSecurityPrivilege = "SeSecurityPrivilege"
|
||||
)
|
||||
|
||||
const (
|
||||
securityAnonymous = iota
|
||||
securityIdentification
|
||||
securityImpersonation
|
||||
securityDelegation
|
||||
)
|
||||
|
||||
var (
|
||||
privNames = make(map[string]uint64)
|
||||
privNameMutex sync.Mutex
|
||||
)
|
||||
|
||||
// PrivilegeError represents an error enabling privileges.
|
||||
type PrivilegeError struct {
|
||||
privileges []uint64
|
||||
}
|
||||
|
||||
func (e *PrivilegeError) Error() string {
|
||||
s := ""
|
||||
if len(e.privileges) > 1 {
|
||||
s = "Could not enable privileges "
|
||||
} else {
|
||||
s = "Could not enable privilege "
|
||||
}
|
||||
for i, p := range e.privileges {
|
||||
if i != 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += `"`
|
||||
s += getPrivilegeName(p)
|
||||
s += `"`
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RunWithPrivilege enables a single privilege for a function call.
|
||||
func RunWithPrivilege(name string, fn func() error) error {
|
||||
return RunWithPrivileges([]string{name}, fn)
|
||||
}
|
||||
|
||||
// RunWithPrivileges enables privileges for a function call.
|
||||
func RunWithPrivileges(names []string, fn func() error) error {
|
||||
privileges, err := mapPrivileges(names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
token, err := newThreadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer releaseThreadToken(token)
|
||||
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fn()
|
||||
}
|
||||
|
||||
func mapPrivileges(names []string) ([]uint64, error) {
|
||||
var privileges []uint64
|
||||
privNameMutex.Lock()
|
||||
defer privNameMutex.Unlock()
|
||||
for _, name := range names {
|
||||
p, ok := privNames[name]
|
||||
if !ok {
|
||||
err := lookupPrivilegeValue("", name, &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privNames[name] = p
|
||||
}
|
||||
privileges = append(privileges, p)
|
||||
}
|
||||
return privileges, nil
|
||||
}
|
||||
|
||||
// EnableProcessPrivileges enables privileges globally for the process.
|
||||
func EnableProcessPrivileges(names []string) error {
|
||||
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
||||
}
|
||||
|
||||
// DisableProcessPrivileges disables privileges globally for the process.
|
||||
func DisableProcessPrivileges(names []string) error {
|
||||
return enableDisableProcessPrivilege(names, 0)
|
||||
}
|
||||
|
||||
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||
privileges, err := mapPrivileges(names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, _ := windows.GetCurrentProcess()
|
||||
var token windows.Token
|
||||
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer token.Close()
|
||||
return adjustPrivileges(token, privileges, action)
|
||||
}
|
||||
|
||||
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||
var b bytes.Buffer
|
||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||
for _, p := range privileges {
|
||||
binary.Write(&b, binary.LittleEndian, p)
|
||||
binary.Write(&b, binary.LittleEndian, action)
|
||||
}
|
||||
prevState := make([]byte, b.Len())
|
||||
reqSize := uint32(0)
|
||||
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
||||
if !success {
|
||||
return err
|
||||
}
|
||||
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||
return &PrivilegeError{privileges}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPrivilegeName(luid uint64) string {
|
||||
var nameBuffer [256]uint16
|
||||
bufSize := uint32(len(nameBuffer))
|
||||
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<unknown privilege %d>", luid)
|
||||
}
|
||||
|
||||
var displayNameBuffer [256]uint16
|
||||
displayBufSize := uint32(len(displayNameBuffer))
|
||||
var langID uint32
|
||||
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
||||
}
|
||||
|
||||
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
||||
}
|
||||
|
||||
func newThreadToken() (windows.Token, error) {
|
||||
err := impersonateSelf(securityImpersonation)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var token windows.Token
|
||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
||||
if err != nil {
|
||||
rerr := revertToSelf()
|
||||
if rerr != nil {
|
||||
panic(rerr)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func releaseThreadToken(h windows.Token) {
|
||||
err := revertToSelf()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
h.Close()
|
||||
}
|
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
|
@ -1,128 +0,0 @@
|
|||
package winio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
reparseTagMountPoint = 0xA0000003
|
||||
reparseTagSymlink = 0xA000000C
|
||||
)
|
||||
|
||||
type reparseDataBuffer struct {
|
||||
ReparseTag uint32
|
||||
ReparseDataLength uint16
|
||||
Reserved uint16
|
||||
SubstituteNameOffset uint16
|
||||
SubstituteNameLength uint16
|
||||
PrintNameOffset uint16
|
||||
PrintNameLength uint16
|
||||
}
|
||||
|
||||
// ReparsePoint describes a Win32 symlink or mount point.
|
||||
type ReparsePoint struct {
|
||||
Target string
|
||||
IsMountPoint bool
|
||||
}
|
||||
|
||||
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||
// mount point reparse point.
|
||||
type UnsupportedReparsePointError struct {
|
||||
Tag uint32
|
||||
}
|
||||
|
||||
func (e *UnsupportedReparsePointError) Error() string {
|
||||
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
||||
}
|
||||
|
||||
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||
// or a mount point.
|
||||
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
||||
tag := binary.LittleEndian.Uint32(b[0:4])
|
||||
return DecodeReparsePointData(tag, b[8:])
|
||||
}
|
||||
|
||||
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
||||
isMountPoint := false
|
||||
switch tag {
|
||||
case reparseTagMountPoint:
|
||||
isMountPoint = true
|
||||
case reparseTagSymlink:
|
||||
default:
|
||||
return nil, &UnsupportedReparsePointError{tag}
|
||||
}
|
||||
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
||||
if !isMountPoint {
|
||||
nameOffset += 4
|
||||
}
|
||||
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
||||
name := make([]uint16, nameLength/2)
|
||||
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
||||
}
|
||||
|
||||
func isDriveLetter(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
}
|
||||
|
||||
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||
// mount point.
|
||||
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||
// Generate an NT path and determine if this is a relative path.
|
||||
var ntTarget string
|
||||
relative := false
|
||||
if strings.HasPrefix(rp.Target, `\\?\`) {
|
||||
ntTarget = `\??\` + rp.Target[4:]
|
||||
} else if strings.HasPrefix(rp.Target, `\\`) {
|
||||
ntTarget = `\??\UNC\` + rp.Target[2:]
|
||||
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
||||
ntTarget = `\??\` + rp.Target
|
||||
} else {
|
||||
ntTarget = rp.Target
|
||||
relative = true
|
||||
}
|
||||
|
||||
// The paths must be NUL-terminated even though they are counted strings.
|
||||
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
||||
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
||||
|
||||
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
||||
size += len(ntTarget16)*2 + len(target16)*2
|
||||
|
||||
tag := uint32(reparseTagMountPoint)
|
||||
if !rp.IsMountPoint {
|
||||
tag = reparseTagSymlink
|
||||
size += 4 // Add room for symlink flags
|
||||
}
|
||||
|
||||
data := reparseDataBuffer{
|
||||
ReparseTag: tag,
|
||||
ReparseDataLength: uint16(size),
|
||||
SubstituteNameOffset: 0,
|
||||
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
||||
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
||||
PrintNameLength: uint16((len(target16) - 1) * 2),
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
binary.Write(&b, binary.LittleEndian, &data)
|
||||
if !rp.IsMountPoint {
|
||||
flags := uint32(0)
|
||||
if relative {
|
||||
flags |= 1
|
||||
}
|
||||
binary.Write(&b, binary.LittleEndian, flags)
|
||||
}
|
||||
|
||||
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||
binary.Write(&b, binary.LittleEndian, target16)
|
||||
return b.Bytes()
|
||||
}
|
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
|
@ -1,98 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||
//sys localFree(mem uintptr) = LocalFree
|
||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||
|
||||
const (
|
||||
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
||||
)
|
||||
|
||||
type AccountLookupError struct {
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *AccountLookupError) Error() string {
|
||||
if e.Name == "" {
|
||||
return "lookup account: empty account name specified"
|
||||
}
|
||||
var s string
|
||||
switch e.Err {
|
||||
case cERROR_NONE_MAPPED:
|
||||
s = "not found"
|
||||
default:
|
||||
s = e.Err.Error()
|
||||
}
|
||||
return "lookup account " + e.Name + ": " + s
|
||||
}
|
||||
|
||||
type SddlConversionError struct {
|
||||
Sddl string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SddlConversionError) Error() string {
|
||||
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// LookupSidByName looks up the SID of an account by name
|
||||
func LookupSidByName(name string) (sid string, err error) {
|
||||
if name == "" {
|
||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
||||
}
|
||||
|
||||
var sidSize, sidNameUse, refDomainSize uint32
|
||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
sidBuffer := make([]byte, sidSize)
|
||||
refDomainBuffer := make([]uint16, refDomainSize)
|
||||
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||
if err != nil {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
var strBuffer *uint16
|
||||
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
||||
if err != nil {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
||||
return sid, nil
|
||||
}
|
||||
|
||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||
var sdBuffer uintptr
|
||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
||||
if err != nil {
|
||||
return nil, &SddlConversionError{sddl, err}
|
||||
}
|
||||
defer localFree(sdBuffer)
|
||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||
var sddl *uint16
|
||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||
// Don't use it.
|
||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
||||
}
|
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
package winio
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
|
427
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
427
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
|
@ -1,427 +0,0 @@
|
|||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
|
||||
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
|
||||
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
|
||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
|
||||
procbind = modws2_32.NewProc("bind")
|
||||
)
|
||||
|
||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||
var _p0 uint32
|
||||
if releaseAll {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||
success = r0 != 0
|
||||
if true {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(str)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||
}
|
||||
|
||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||
len = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func impersonateSelf(level uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||
}
|
||||
|
||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeValue(_p0, _p1, luid)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||
var _p0 uint32
|
||||
if openAsSelf {
|
||||
_p0 = 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func revertToSelf() (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||
var _p0 *byte
|
||||
if len(b) > 0 {
|
||||
_p0 = &b[0]
|
||||
}
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||
var _p0 *byte
|
||||
if len(b) > 0 {
|
||||
_p0 = &b[0]
|
||||
}
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
if newport == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||
}
|
||||
|
||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCurrentThread() (h syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||
h = syscall.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
||||
ptr = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func localFree(mem uintptr) {
|
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
|
||||
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
|
||||
status = ntstatus(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
|
||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
||||
if r0 != 0 {
|
||||
winerr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
|
||||
var _p0 uint32
|
||||
if wait {
|
||||
_p0 = 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
||||
if r1 == socketError {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
3
vendor/github.com/ProtonMail/go-crypto/AUTHORS
generated
vendored
3
vendor/github.com/ProtonMail/go-crypto/AUTHORS
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/AUTHORS.
|
3
vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS
generated
vendored
3
vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at https://tip.golang.org/CONTRIBUTORS.
|
27
vendor/github.com/ProtonMail/go-crypto/LICENSE
generated
vendored
27
vendor/github.com/ProtonMail/go-crypto/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
vendor/github.com/ProtonMail/go-crypto/PATENTS
generated
vendored
22
vendor/github.com/ProtonMail/go-crypto/PATENTS
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
381
vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.go
generated
vendored
381
vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.go
generated
vendored
|
@ -1,381 +0,0 @@
|
|||
package bitcurves
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Copyright 2011 ThePiachu. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bitelliptic implements several Koblitz elliptic curves over prime
|
||||
// fields.
|
||||
|
||||
// This package operates, internally, on Jacobian coordinates. For a given
|
||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
||||
// calculation can be performed within the transform (as in ScalarMult and
|
||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
||||
// reverse the transform than to operate in affine coordinates.
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"io"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A BitCurve represents a Koblitz Curve with a=0.
|
||||
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
|
||||
type BitCurve struct {
|
||||
Name string
|
||||
P *big.Int // the order of the underlying field
|
||||
N *big.Int // the order of the base point
|
||||
B *big.Int // the constant of the BitCurve equation
|
||||
Gx, Gy *big.Int // (x,y) of the base point
|
||||
BitSize int // the size of the underlying field
|
||||
}
|
||||
|
||||
// Params returns the parameters of the given BitCurve (see BitCurve struct)
|
||||
func (bitCurve *BitCurve) Params() (cp *elliptic.CurveParams) {
|
||||
cp = new(elliptic.CurveParams)
|
||||
cp.Name = bitCurve.Name
|
||||
cp.P = bitCurve.P
|
||||
cp.N = bitCurve.N
|
||||
cp.Gx = bitCurve.Gx
|
||||
cp.Gy = bitCurve.Gy
|
||||
cp.BitSize = bitCurve.BitSize
|
||||
return cp
|
||||
}
|
||||
|
||||
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
|
||||
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
// y² = x³ + b
|
||||
y2 := new(big.Int).Mul(y, y) //y²
|
||||
y2.Mod(y2, bitCurve.P) //y²%P
|
||||
|
||||
x3 := new(big.Int).Mul(x, x) //x²
|
||||
x3.Mul(x3, x) //x³
|
||||
|
||||
x3.Add(x3, bitCurve.B) //x³+B
|
||||
x3.Mod(x3, bitCurve.P) //(x³+B)%P
|
||||
|
||||
return x3.Cmp(y2) == 0
|
||||
}
|
||||
|
||||
// affineFromJacobian reverses the Jacobian transform. See the comment at the
|
||||
// top of the file.
|
||||
func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
|
||||
if z.Cmp(big.NewInt(0)) == 0 {
|
||||
panic("bitcurve: Can't convert to affine with Jacobian Z = 0")
|
||||
}
|
||||
// x = YZ^2 mod P
|
||||
zinv := new(big.Int).ModInverse(z, bitCurve.P)
|
||||
zinvsq := new(big.Int).Mul(zinv, zinv)
|
||||
|
||||
xOut = new(big.Int).Mul(x, zinvsq)
|
||||
xOut.Mod(xOut, bitCurve.P)
|
||||
// y = YZ^3 mod P
|
||||
zinvsq.Mul(zinvsq, zinv)
|
||||
yOut = new(big.Int).Mul(y, zinvsq)
|
||||
yOut.Mod(yOut, bitCurve.P)
|
||||
return xOut, yOut
|
||||
}
|
||||
|
||||
// Add returns the sum of (x1,y1) and (x2,y2)
|
||||
func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
z := new(big.Int).SetInt64(1)
|
||||
x, y, z := bitCurve.addJacobian(x1, y1, z, x2, y2, z)
|
||||
return bitCurve.affineFromJacobian(x, y, z)
|
||||
}
|
||||
|
||||
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
|
||||
// (x2, y2, z2) and returns their sum, also in Jacobian form.
|
||||
func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
z1z1 := new(big.Int).Mul(z1, z1)
|
||||
z1z1.Mod(z1z1, bitCurve.P)
|
||||
z2z2 := new(big.Int).Mul(z2, z2)
|
||||
z2z2.Mod(z2z2, bitCurve.P)
|
||||
|
||||
u1 := new(big.Int).Mul(x1, z2z2)
|
||||
u1.Mod(u1, bitCurve.P)
|
||||
u2 := new(big.Int).Mul(x2, z1z1)
|
||||
u2.Mod(u2, bitCurve.P)
|
||||
h := new(big.Int).Sub(u2, u1)
|
||||
if h.Sign() == -1 {
|
||||
h.Add(h, bitCurve.P)
|
||||
}
|
||||
i := new(big.Int).Lsh(h, 1)
|
||||
i.Mul(i, i)
|
||||
j := new(big.Int).Mul(h, i)
|
||||
|
||||
s1 := new(big.Int).Mul(y1, z2)
|
||||
s1.Mul(s1, z2z2)
|
||||
s1.Mod(s1, bitCurve.P)
|
||||
s2 := new(big.Int).Mul(y2, z1)
|
||||
s2.Mul(s2, z1z1)
|
||||
s2.Mod(s2, bitCurve.P)
|
||||
r := new(big.Int).Sub(s2, s1)
|
||||
if r.Sign() == -1 {
|
||||
r.Add(r, bitCurve.P)
|
||||
}
|
||||
r.Lsh(r, 1)
|
||||
v := new(big.Int).Mul(u1, i)
|
||||
|
||||
x3 := new(big.Int).Set(r)
|
||||
x3.Mul(x3, x3)
|
||||
x3.Sub(x3, j)
|
||||
x3.Sub(x3, v)
|
||||
x3.Sub(x3, v)
|
||||
x3.Mod(x3, bitCurve.P)
|
||||
|
||||
y3 := new(big.Int).Set(r)
|
||||
v.Sub(v, x3)
|
||||
y3.Mul(y3, v)
|
||||
s1.Mul(s1, j)
|
||||
s1.Lsh(s1, 1)
|
||||
y3.Sub(y3, s1)
|
||||
y3.Mod(y3, bitCurve.P)
|
||||
|
||||
z3 := new(big.Int).Add(z1, z2)
|
||||
z3.Mul(z3, z3)
|
||||
z3.Sub(z3, z1z1)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, bitCurve.P)
|
||||
}
|
||||
z3.Sub(z3, z2z2)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, bitCurve.P)
|
||||
}
|
||||
z3.Mul(z3, h)
|
||||
z3.Mod(z3, bitCurve.P)
|
||||
|
||||
return x3, y3, z3
|
||||
}
|
||||
|
||||
// Double returns 2*(x,y)
|
||||
func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
z1 := new(big.Int).SetInt64(1)
|
||||
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1))
|
||||
}
|
||||
|
||||
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
|
||||
// returns its double, also in Jacobian form.
|
||||
func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
|
||||
a := new(big.Int).Mul(x, x) //X1²
|
||||
b := new(big.Int).Mul(y, y) //Y1²
|
||||
c := new(big.Int).Mul(b, b) //B²
|
||||
|
||||
d := new(big.Int).Add(x, b) //X1+B
|
||||
d.Mul(d, d) //(X1+B)²
|
||||
d.Sub(d, a) //(X1+B)²-A
|
||||
d.Sub(d, c) //(X1+B)²-A-C
|
||||
d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C)
|
||||
|
||||
e := new(big.Int).Mul(big.NewInt(3), a) //3*A
|
||||
f := new(big.Int).Mul(e, e) //E²
|
||||
|
||||
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
|
||||
x3.Sub(f, x3) //F-2*D
|
||||
x3.Mod(x3, bitCurve.P)
|
||||
|
||||
y3 := new(big.Int).Sub(d, x3) //D-X3
|
||||
y3.Mul(e, y3) //E*(D-X3)
|
||||
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
|
||||
y3.Mod(y3, bitCurve.P)
|
||||
|
||||
z3 := new(big.Int).Mul(y, z) //Y1*Z1
|
||||
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
|
||||
z3.Mod(z3, bitCurve.P)
|
||||
|
||||
return x3, y3, z3
|
||||
}
|
||||
|
||||
//TODO: double check if it is okay
|
||||
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
|
||||
// We have a slight problem in that the identity of the group (the
|
||||
// point at infinity) cannot be represented in (x, y) form on a finite
|
||||
// machine. Thus the standard add/double algorithm has to be tweaked
|
||||
// slightly: our initial state is not the identity, but x, and we
|
||||
// ignore the first true bit in |k|. If we don't find any true bits in
|
||||
// |k|, then we return nil, nil, because we cannot return the identity
|
||||
// element.
|
||||
|
||||
Bz := new(big.Int).SetInt64(1)
|
||||
x := Bx
|
||||
y := By
|
||||
z := Bz
|
||||
|
||||
seenFirstTrue := false
|
||||
for _, byte := range k {
|
||||
for bitNum := 0; bitNum < 8; bitNum++ {
|
||||
if seenFirstTrue {
|
||||
x, y, z = bitCurve.doubleJacobian(x, y, z)
|
||||
}
|
||||
if byte&0x80 == 0x80 {
|
||||
if !seenFirstTrue {
|
||||
seenFirstTrue = true
|
||||
} else {
|
||||
x, y, z = bitCurve.addJacobian(Bx, By, Bz, x, y, z)
|
||||
}
|
||||
}
|
||||
byte <<= 1
|
||||
}
|
||||
}
|
||||
|
||||
if !seenFirstTrue {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return bitCurve.affineFromJacobian(x, y, z)
|
||||
}
|
||||
|
||||
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||
// an integer in big-endian form.
|
||||
func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k)
|
||||
}
|
||||
|
||||
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
|
||||
|
||||
//TODO: double check if it is okay
|
||||
// GenerateKey returns a public/private key pair. The private key is generated
|
||||
// using the given reader, which must return random data.
|
||||
func (bitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
|
||||
byteLen := (bitCurve.BitSize + 7) >> 3
|
||||
priv = make([]byte, byteLen)
|
||||
|
||||
for x == nil {
|
||||
_, err = io.ReadFull(rand, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// We have to mask off any excess bits in the case that the size of the
|
||||
// underlying field is not a whole number of bytes.
|
||||
priv[0] &= mask[bitCurve.BitSize%8]
|
||||
// This is because, in tests, rand will return all zeros and we don't
|
||||
// want to get the point at infinity and loop forever.
|
||||
priv[1] ^= 0x42
|
||||
x, y = bitCurve.ScalarBaseMult(priv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
|
||||
// X9.62.
|
||||
func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
|
||||
byteLen := (bitCurve.BitSize + 7) >> 3
|
||||
|
||||
ret := make([]byte, 1+2*byteLen)
|
||||
ret[0] = 4 // uncompressed point
|
||||
|
||||
xBytes := x.Bytes()
|
||||
copy(ret[1+byteLen-len(xBytes):], xBytes)
|
||||
yBytes := y.Bytes()
|
||||
copy(ret[1+2*byteLen-len(yBytes):], yBytes)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
|
||||
// error, x = nil.
|
||||
func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
|
||||
byteLen := (bitCurve.BitSize + 7) >> 3
|
||||
if len(data) != 1+2*byteLen {
|
||||
return
|
||||
}
|
||||
if data[0] != 4 { // uncompressed form
|
||||
return
|
||||
}
|
||||
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
|
||||
y = new(big.Int).SetBytes(data[1+byteLen:])
|
||||
return
|
||||
}
|
||||
|
||||
//curve parameters taken from:
|
||||
//http://www.secg.org/collateral/sec2_final.pdf
|
||||
|
||||
var initonce sync.Once
|
||||
var secp160k1 *BitCurve
|
||||
var secp192k1 *BitCurve
|
||||
var secp224k1 *BitCurve
|
||||
var secp256k1 *BitCurve
|
||||
|
||||
func initAll() {
|
||||
initS160()
|
||||
initS192()
|
||||
initS224()
|
||||
initS256()
|
||||
}
|
||||
|
||||
func initS160() {
|
||||
// See SEC 2 section 2.4.1
|
||||
secp160k1 = new(BitCurve)
|
||||
secp160k1.Name = "secp160k1"
|
||||
secp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16)
|
||||
secp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16)
|
||||
secp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16)
|
||||
secp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16)
|
||||
secp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16)
|
||||
secp160k1.BitSize = 160
|
||||
}
|
||||
|
||||
func initS192() {
|
||||
// See SEC 2 section 2.5.1
|
||||
secp192k1 = new(BitCurve)
|
||||
secp192k1.Name = "secp192k1"
|
||||
secp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16)
|
||||
secp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16)
|
||||
secp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16)
|
||||
secp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16)
|
||||
secp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16)
|
||||
secp192k1.BitSize = 192
|
||||
}
|
||||
|
||||
func initS224() {
|
||||
// See SEC 2 section 2.6.1
|
||||
secp224k1 = new(BitCurve)
|
||||
secp224k1.Name = "secp224k1"
|
||||
secp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16)
|
||||
secp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16)
|
||||
secp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16)
|
||||
secp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16)
|
||||
secp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16)
|
||||
secp224k1.BitSize = 224
|
||||
}
|
||||
|
||||
func initS256() {
|
||||
// See SEC 2 section 2.7.1
|
||||
secp256k1 = new(BitCurve)
|
||||
secp256k1.Name = "secp256k1"
|
||||
secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
|
||||
secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
|
||||
secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
|
||||
secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
|
||||
secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
|
||||
secp256k1.BitSize = 256
|
||||
}
|
||||
|
||||
// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1)
|
||||
func S160() *BitCurve {
|
||||
initonce.Do(initAll)
|
||||
return secp160k1
|
||||
}
|
||||
|
||||
// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1)
|
||||
func S192() *BitCurve {
|
||||
initonce.Do(initAll)
|
||||
return secp192k1
|
||||
}
|
||||
|
||||
// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1)
|
||||
func S224() *BitCurve {
|
||||
initonce.Do(initAll)
|
||||
return secp224k1
|
||||
}
|
||||
|
||||
// S256 returns a BitCurve which implements bitcurves (see SEC 2 section 2.7.1)
|
||||
func S256() *BitCurve {
|
||||
initonce.Do(initAll)
|
||||
return secp256k1
|
||||
}
|
134
vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.go
generated
vendored
134
vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.go
generated
vendored
|
@ -1,134 +0,0 @@
|
|||
// Package brainpool implements Brainpool elliptic curves.
|
||||
// Implementation of rcurves is from github.com/ebfe/brainpool
|
||||
// Note that these curves are implemented with naive, non-constant time operations
|
||||
// and are likely not suitable for enviroments where timing attacks are a concern.
|
||||
package brainpool
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
p256t1, p384t1, p512t1 *elliptic.CurveParams
|
||||
p256r1, p384r1, p512r1 *rcurve
|
||||
)
|
||||
|
||||
func initAll() {
|
||||
initP256t1()
|
||||
initP384t1()
|
||||
initP512t1()
|
||||
initP256r1()
|
||||
initP384r1()
|
||||
initP512r1()
|
||||
}
|
||||
|
||||
func initP256t1() {
|
||||
p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"}
|
||||
p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16)
|
||||
p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16)
|
||||
p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)
|
||||
p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16)
|
||||
p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16)
|
||||
p256t1.BitSize = 256
|
||||
}
|
||||
|
||||
func initP256r1() {
|
||||
twisted := p256t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP256r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16)
|
||||
z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16)
|
||||
p256r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
func initP384t1() {
|
||||
p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"}
|
||||
p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16)
|
||||
p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16)
|
||||
p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)
|
||||
p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16)
|
||||
p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16)
|
||||
p384t1.BitSize = 384
|
||||
}
|
||||
|
||||
func initP384r1() {
|
||||
twisted := p384t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP384r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16)
|
||||
z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16)
|
||||
p384r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
func initP512t1() {
|
||||
p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"}
|
||||
p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16)
|
||||
p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16)
|
||||
p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)
|
||||
p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16)
|
||||
p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16)
|
||||
p512t1.BitSize = 512
|
||||
}
|
||||
|
||||
func initP512r1() {
|
||||
twisted := p512t1
|
||||
params := &elliptic.CurveParams{
|
||||
Name: "brainpoolP512r1",
|
||||
P: twisted.P,
|
||||
N: twisted.N,
|
||||
BitSize: twisted.BitSize,
|
||||
}
|
||||
params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16)
|
||||
params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16)
|
||||
z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16)
|
||||
p512r1 = newrcurve(twisted, params, z)
|
||||
}
|
||||
|
||||
// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4)
|
||||
func P256t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p256t1
|
||||
}
|
||||
|
||||
// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)
|
||||
func P256r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p256r1
|
||||
}
|
||||
|
||||
// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6)
|
||||
func P384t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p384t1
|
||||
}
|
||||
|
||||
// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)
|
||||
func P384r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p384r1
|
||||
}
|
||||
|
||||
// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7)
|
||||
func P512t1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p512t1
|
||||
}
|
||||
|
||||
// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)
|
||||
func P512r1() elliptic.Curve {
|
||||
once.Do(initAll)
|
||||
return p512r1
|
||||
}
|
83
vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.go
generated
vendored
83
vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.go
generated
vendored
|
@ -1,83 +0,0 @@
|
|||
package brainpool
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var _ elliptic.Curve = (*rcurve)(nil)
|
||||
|
||||
type rcurve struct {
|
||||
twisted elliptic.Curve
|
||||
params *elliptic.CurveParams
|
||||
z *big.Int
|
||||
zinv *big.Int
|
||||
z2 *big.Int
|
||||
z3 *big.Int
|
||||
zinv2 *big.Int
|
||||
zinv3 *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
two = big.NewInt(2)
|
||||
three = big.NewInt(3)
|
||||
)
|
||||
|
||||
func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve {
|
||||
zinv := new(big.Int).ModInverse(z, params.P)
|
||||
return &rcurve{
|
||||
twisted: twisted,
|
||||
params: params,
|
||||
z: z,
|
||||
zinv: zinv,
|
||||
z2: new(big.Int).Exp(z, two, params.P),
|
||||
z3: new(big.Int).Exp(z, three, params.P),
|
||||
zinv2: new(big.Int).Exp(zinv, two, params.P),
|
||||
zinv3: new(big.Int).Exp(zinv, three, params.P),
|
||||
}
|
||||
}
|
||||
|
||||
func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) {
|
||||
var tx, ty big.Int
|
||||
tx.Mul(x, curve.z2)
|
||||
tx.Mod(&tx, curve.params.P)
|
||||
ty.Mul(y, curve.z3)
|
||||
ty.Mod(&ty, curve.params.P)
|
||||
return &tx, &ty
|
||||
}
|
||||
|
||||
func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) {
|
||||
var x, y big.Int
|
||||
x.Mul(tx, curve.zinv2)
|
||||
x.Mod(&x, curve.params.P)
|
||||
y.Mul(ty, curve.zinv3)
|
||||
y.Mod(&y, curve.params.P)
|
||||
return &x, &y
|
||||
}
|
||||
|
||||
func (curve *rcurve) Params() *elliptic.CurveParams {
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve *rcurve) IsOnCurve(x, y *big.Int) bool {
|
||||
return curve.twisted.IsOnCurve(curve.toTwisted(x, y))
|
||||
}
|
||||
|
||||
func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
|
||||
tx1, ty1 := curve.toTwisted(x1, y1)
|
||||
tx2, ty2 := curve.toTwisted(x2, y2)
|
||||
return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2))
|
||||
}
|
||||
|
||||
func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) {
|
||||
return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1)))
|
||||
}
|
||||
|
||||
func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) {
|
||||
tx1, ty1 := curve.toTwisted(x1, y1)
|
||||
return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar))
|
||||
}
|
||||
|
||||
func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
|
||||
return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar))
|
||||
}
|
162
vendor/github.com/ProtonMail/go-crypto/eax/eax.go
generated
vendored
162
vendor/github.com/ProtonMail/go-crypto/eax/eax.go
generated
vendored
|
@ -1,162 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
// Package eax provides an implementation of the EAX
|
||||
// (encrypt-authenticate-translate) mode of operation, as described in
|
||||
// Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS
|
||||
// AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY."
|
||||
// In FSE'04, volume 3017 of LNCS, 2004
|
||||
package eax
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"github.com/ProtonMail/go-crypto/internal/byteutil"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTagSize = 16
|
||||
defaultNonceSize = 16
|
||||
)
|
||||
|
||||
type eax struct {
|
||||
block cipher.Block // Only AES-{128, 192, 256} supported
|
||||
tagSize int // At least 12 bytes recommended
|
||||
nonceSize int
|
||||
}
|
||||
|
||||
func (e *eax) NonceSize() int {
|
||||
return e.nonceSize
|
||||
}
|
||||
|
||||
func (e *eax) Overhead() int {
|
||||
return e.tagSize
|
||||
}
|
||||
|
||||
// NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and
|
||||
// tag lengths. Supports {128, 192, 256}- bit key length.
|
||||
func NewEAX(block cipher.Block) (cipher.AEAD, error) {
|
||||
return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
|
||||
}
|
||||
|
||||
// NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and
|
||||
// given nonce and tag lengths in bytes. Panics on zero nonceSize and
|
||||
// exceedingly long tags.
|
||||
//
|
||||
// It is recommended to use at least 12 bytes as tag length (see, for instance,
|
||||
// NIST SP 800-38D).
|
||||
//
|
||||
// Only to be used for compatibility with existing cryptosystems with
|
||||
// non-standard parameters. For all other cases, prefer NewEAX.
|
||||
func NewEAXWithNonceAndTagSize(
|
||||
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
|
||||
if nonceSize < 1 {
|
||||
return nil, eaxError("Cannot initialize EAX with nonceSize = 0")
|
||||
}
|
||||
if tagSize > block.BlockSize() {
|
||||
return nil, eaxError("Custom tag length exceeds blocksize")
|
||||
}
|
||||
return &eax{
|
||||
block: block,
|
||||
tagSize: tagSize,
|
||||
nonceSize: nonceSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte {
|
||||
if len(nonce) > e.nonceSize {
|
||||
panic("crypto/eax: Nonce too long for this instance")
|
||||
}
|
||||
ret, out := byteutil.SliceForAppend(dst, len(plaintext) + e.tagSize)
|
||||
omacNonce := e.omacT(0, nonce)
|
||||
omacAdata := e.omacT(1, adata)
|
||||
|
||||
// Encrypt message using CTR mode and omacNonce as IV
|
||||
ctr := cipher.NewCTR(e.block, omacNonce)
|
||||
ciphertextData := out[:len(plaintext)]
|
||||
ctr.XORKeyStream(ciphertextData, plaintext)
|
||||
|
||||
omacCiphertext := e.omacT(2, ciphertextData)
|
||||
|
||||
tag := out[len(plaintext):]
|
||||
for i := 0; i < e.tagSize; i++ {
|
||||
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e* eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
|
||||
if len(nonce) > e.nonceSize {
|
||||
panic("crypto/eax: Nonce too long for this instance")
|
||||
}
|
||||
if len(ciphertext) < e.tagSize {
|
||||
return nil, eaxError("Ciphertext shorter than tag length")
|
||||
}
|
||||
sep := len(ciphertext) - e.tagSize
|
||||
|
||||
// Compute tag
|
||||
omacNonce := e.omacT(0, nonce)
|
||||
omacAdata := e.omacT(1, adata)
|
||||
omacCiphertext := e.omacT(2, ciphertext[:sep])
|
||||
|
||||
tag := make([]byte, e.tagSize)
|
||||
for i := 0; i < e.tagSize; i++ {
|
||||
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
|
||||
}
|
||||
|
||||
// Compare tags
|
||||
if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 {
|
||||
return nil, eaxError("Tag authentication failed")
|
||||
}
|
||||
|
||||
// Decrypt ciphertext
|
||||
ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
|
||||
ctr := cipher.NewCTR(e.block, omacNonce)
|
||||
ctr.XORKeyStream(out, ciphertext[:sep])
|
||||
|
||||
return ret[:sep], nil
|
||||
}
|
||||
|
||||
// Tweakable OMAC - Calls OMAC_K([t]_n || plaintext)
|
||||
func (e *eax) omacT(t byte, plaintext []byte) []byte {
|
||||
blockSize := e.block.BlockSize()
|
||||
byteT := make([]byte, blockSize)
|
||||
byteT[blockSize-1] = t
|
||||
concat := append(byteT, plaintext...)
|
||||
return e.omac(concat)
|
||||
}
|
||||
|
||||
func (e *eax) omac(plaintext []byte) []byte {
|
||||
blockSize := e.block.BlockSize()
|
||||
// L ← E_K(0^n); B ← 2L; P ← 4L
|
||||
L := make([]byte, blockSize)
|
||||
e.block.Encrypt(L, L)
|
||||
B := byteutil.GfnDouble(L)
|
||||
P := byteutil.GfnDouble(B)
|
||||
|
||||
// CBC with IV = 0
|
||||
cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize))
|
||||
padded := e.pad(plaintext, B, P)
|
||||
cbcCiphertext := make([]byte, len(padded))
|
||||
cbc.CryptBlocks(cbcCiphertext, padded)
|
||||
|
||||
return cbcCiphertext[len(cbcCiphertext)-blockSize:]
|
||||
}
|
||||
|
||||
func (e *eax) pad(plaintext, B, P []byte) []byte {
|
||||
// if |M| in {n, 2n, 3n, ...}
|
||||
blockSize := e.block.BlockSize()
|
||||
if len(plaintext) != 0 && len(plaintext)%blockSize == 0 {
|
||||
return byteutil.RightXor(plaintext, B)
|
||||
}
|
||||
|
||||
// else return (M || 1 || 0^(n−1−(|M| % n))) xor→ P
|
||||
ending := make([]byte, blockSize-len(plaintext)%blockSize)
|
||||
ending[0] = 0x80
|
||||
padded := append(plaintext, ending...)
|
||||
return byteutil.RightXor(padded, P)
|
||||
}
|
||||
|
||||
func eaxError(err string) error {
|
||||
return errors.New("crypto/eax: " + err)
|
||||
}
|
58
vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.go
generated
vendored
58
vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.go
generated
vendored
|
@ -1,58 +0,0 @@
|
|||
package eax
|
||||
|
||||
// Test vectors from
|
||||
// https://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf
|
||||
var testVectors = []struct {
|
||||
msg, key, nonce, header, ciphertext string
|
||||
}{
|
||||
{"",
|
||||
"233952DEE4D5ED5F9B9C6D6FF80FF478",
|
||||
"62EC67F9C3A4A407FCB2A8C49031A8B3",
|
||||
"6BFB914FD07EAE6B",
|
||||
"E037830E8389F27B025A2D6527E79D01"},
|
||||
{"F7FB",
|
||||
"91945D3F4DCBEE0BF45EF52255F095A4",
|
||||
"BECAF043B0A23D843194BA972C66DEBD",
|
||||
"FA3BFD4806EB53FA",
|
||||
"19DD5C4C9331049D0BDAB0277408F67967E5"},
|
||||
{"1A47CB4933",
|
||||
"01F74AD64077F2E704C0F60ADA3DD523",
|
||||
"70C3DB4F0D26368400A10ED05D2BFF5E",
|
||||
"234A3463C1264AC6",
|
||||
"D851D5BAE03A59F238A23E39199DC9266626C40F80"},
|
||||
{"481C9E39B1",
|
||||
"D07CF6CBB7F313BDDE66B727AFD3C5E8",
|
||||
"8408DFFF3C1A2B1292DC199E46B7D617",
|
||||
"33CCE2EABFF5A79D",
|
||||
"632A9D131AD4C168A4225D8E1FF755939974A7BEDE"},
|
||||
{"40D0C07DA5E4",
|
||||
"35B6D0580005BBC12B0587124557D2C2",
|
||||
"FDB6B06676EEDC5C61D74276E1F8E816",
|
||||
"AEB96EAEBE2970E9",
|
||||
"071DFE16C675CB0677E536F73AFE6A14B74EE49844DD"},
|
||||
{"4DE3B35C3FC039245BD1FB7D",
|
||||
"BD8E6E11475E60B268784C38C62FEB22",
|
||||
"6EAC5C93072D8E8513F750935E46DA1B",
|
||||
"D4482D1CA78DCE0F",
|
||||
"835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F"},
|
||||
{"8B0A79306C9CE7ED99DAE4F87F8DD61636",
|
||||
"7C77D6E813BED5AC98BAA417477A2E7D",
|
||||
"1A8C98DCD73D38393B2BF1569DEEFC19",
|
||||
"65D2017990D62528",
|
||||
"02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2"},
|
||||
{"1BDA122BCE8A8DBAF1877D962B8592DD2D56",
|
||||
"5FFF20CAFAB119CA2FC73549E20F5B0D",
|
||||
"DDE59B97D722156D4D9AFF2BC7559826",
|
||||
"54B9F04E6A09189A",
|
||||
"2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A"},
|
||||
{"6CF36720872B8513F6EAB1A8A44438D5EF11",
|
||||
"A4A4782BCFFD3EC5E7EF6D8C34A56123",
|
||||
"B781FCF2F75FA5A8DE97A9CA48E522EC",
|
||||
"899A175897561D7E",
|
||||
"0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700"},
|
||||
{"CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7",
|
||||
"8395FCF1E95BEBD697BD010BC766AAC3",
|
||||
"22E7ADD93CFC6393C57EC0B3C17D6B44",
|
||||
"126735FCC320D25A",
|
||||
"CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E"},
|
||||
}
|
131
vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.go
generated
vendored
131
vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.go
generated
vendored
|
@ -1,131 +0,0 @@
|
|||
// These vectors include key length in {128, 192, 256}, tag size 128, and
|
||||
// random nonce, header, and plaintext lengths.
|
||||
|
||||
// This file was automatically generated.
|
||||
|
||||
package eax
|
||||
|
||||
var randomVectors = []struct {
|
||||
key, nonce, header, plaintext, ciphertext string
|
||||
}{
|
||||
{"DFDE093F36B0356E5A81F609786982E3",
|
||||
"1D8AC604419001816905BA72B14CED7E",
|
||||
"152A1517A998D7A24163FCDD146DE81AC347C8B97088F502093C1ABB8F6E33D9A219C34D7603A18B1F5ABE02E56661B7D7F67E81EC08C1302EF38D80A859486D450E94A4F26AD9E68EEBBC0C857A0FC5CF9E641D63D565A7E361BC8908F5A8DC8FD6",
|
||||
"1C8EAAB71077FE18B39730A3156ADE29C5EE824C7EE86ED2A253B775603FB237116E654F6FEC588DD27F523A0E01246FE73FE348491F2A8E9ABC6CA58D663F71CDBCF4AD798BE46C42AE6EE8B599DB44A1A48D7BBBBA0F7D2750181E1C5E66967F7D57CBD30AFBDA5727",
|
||||
"79E7E150934BBEBF7013F61C60462A14D8B15AF7A248AFB8A344EF021C1500E16666891D6E973D8BB56B71A371F12CA34660C4410C016982B20F547E3762A58B7BF4F20236CADCF559E2BE7D783B13723B2741FC7CDC8997D839E39A3DDD2BADB96743DD7049F1BDB0516A262869915B3F70498AFB7B191BF960"},
|
||||
{"F10619EF02E5D94D7550EB84ED364A21",
|
||||
"8DC0D4F2F745BBAE835CC5574B942D20",
|
||||
"FE561358F2E8DF7E1024FF1AE9A8D36EBD01352214505CB99D644777A8A1F6027FA2BDBFC529A9B91136D5F2416CFC5F0F4EC3A1AFD32BDDA23CA504C5A5CB451785FABF4DFE4CD50D817491991A60615B30286361C100A95D1712F2A45F8E374461F4CA2B",
|
||||
"D7B5A971FC219631D30EFC3664AE3127D9CF3097DAD9C24AC7905D15E8D9B25B026B31D68CAE00975CDB81EB1FD96FD5E1A12E2BB83FA25F1B1D91363457657FC03875C27F2946C5",
|
||||
"2F336ED42D3CC38FC61660C4CD60BA4BD438B05F5965D8B7B399D2E7167F5D34F792D318F94DB15D67463AC449E13D568CC09BFCE32A35EE3EE96A041927680AE329811811E27F2D1E8E657707AF99BA96D13A478D695D59"},
|
||||
{"429F514EFC64D98A698A9247274CFF45",
|
||||
"976AA5EB072F912D126ACEBC954FEC38",
|
||||
"A71D89DC5B6CEDBB7451A27C3C2CAE09126DB4C421",
|
||||
"5632FE62AB1DC549D54D3BC3FC868ACCEDEFD9ECF5E9F8",
|
||||
"848AE4306CA8C7F416F8707625B7F55881C0AB430353A5C967CDA2DA787F581A70E34DBEBB2385"},
|
||||
{"398138F309085F47F8457CDF53895A63",
|
||||
"F8A8A7F2D28E5FFF7BBC2F24353F7A36",
|
||||
"5D633C21BA7764B8855CAB586F3746E236AD486039C83C6B56EFA9C651D38A41D6B20DAEE3418BFEA44B8BD6",
|
||||
"A3BBAA91920AF5E10659818B1B3B300AC79BFC129C8329E75251F73A66D3AE0128EB91D5031E0A65C329DB7D1E9C0493E268",
|
||||
"D078097267606E5FB07CFB7E2B4B718172A82C6A4CEE65D549A4DFB9838003BD2FBF64A7A66988AC1A632FD88F9E9FBB57C5A78AD2E086EACBA3DB68511D81C2970A"},
|
||||
{"7A4151EBD3901B42CBA45DAFB2E931BA",
|
||||
"0FC88ACEE74DD538040321C330974EB8",
|
||||
"250464FB04733BAB934C59E6AD2D6AE8D662CBCFEFBE61E5A308D4211E58C4C25935B72C69107722E946BFCBF416796600542D76AEB73F2B25BF53BAF97BDEB36ED3A7A51C31E7F170EB897457E7C17571D1BA0A908954E9",
|
||||
"88C41F3EBEC23FAB8A362D969CAC810FAD4F7CA6A7F7D0D44F060F92E37E1183768DD4A8C733F71C96058D362A39876D183B86C103DE",
|
||||
"74A25B2182C51096D48A870D80F18E1CE15867778E34FCBA6BD7BFB3739FDCD42AD0F2D9F4EBA29085285C6048C15BCE5E5166F1F962D3337AA88E6062F05523029D0A7F0BF9"},
|
||||
{"BFB147E1CD5459424F8C0271FC0E0DC5",
|
||||
"EABCC126442BF373969EA3015988CC45",
|
||||
"4C0880E1D71AA2C7",
|
||||
"BE1B5EC78FBF73E7A6682B21BA7E0E5D2D1C7ABE",
|
||||
"5660D7C1380E2F306895B1402CB2D6C37876504276B414D120F4CF92FDDDBB293A238EA0"},
|
||||
{"595DD6F52D18BC2CA8EB4EDAA18D9FA3",
|
||||
"0F84B5D36CF4BC3B863313AF3B4D2E97",
|
||||
"30AE6CC5F99580F12A779D98BD379A60948020C0B6FBD5746B30BA3A15C6CD33DAF376C70A9F15B6C0EB410A93161F7958AE23",
|
||||
"8EF3687A1642B070970B0B91462229D1D76ABC154D18211F7152AA9FF368",
|
||||
"317C1DDB11417E5A9CC4DDE7FDFF6659A5AC4B31DE025212580A05CDAC6024D3E4AE7C2966E52B9129E9ECDBED86"},
|
||||
{"44E6F2DC8FDC778AD007137D11410F50",
|
||||
"270A237AD977F7187AA6C158A0BAB24F",
|
||||
"509B0F0EB12E2AA5C5BA2DE553C07FAF4CE0C9E926531AA709A3D6224FCB783ACCF1559E10B1123EBB7D52E8AB54E6B5352A9ED0D04124BF0E9D9BACFD7E32B817B2E625F5EE94A64EDE9E470DE7FE6886C19B294F9F828209FE257A78",
|
||||
"8B3D7815DF25618A5D0C55A601711881483878F113A12EC36CF64900549A3199555528559DC118F789788A55FAFD944E6E99A9CA3F72F238CD3F4D88223F7A745992B3FAED1848",
|
||||
"1CC00D79F7AD82FDA71B58D286E5F34D0CC4CEF30704E771CC1E50746BDF83E182B078DB27149A42BAE619DF0F85B0B1090AD55D3B4471B0D6F6ECCD09C8F876B30081F0E7537A9624F8AAF29DA85E324122EFB4D68A56"},
|
||||
{"BB7BC352A03044B4428D8DBB4B0701FDEC4649FD17B81452",
|
||||
"8B4BBE26CCD9859DCD84884159D6B0A4",
|
||||
"2212BEB0E78E0F044A86944CF33C8D5C80D9DBE1034BF3BCF73611835C7D3A52F5BD2D81B68FD681B68540A496EE5DA16FD8AC8824E60E1EC2042BE28FB0BFAD4E4B03596446BDD8C37D936D9B3D5295BE19F19CF5ACE1D33A46C952CE4DE5C12F92C1DD051E04AEED",
|
||||
"9037234CC44FFF828FABED3A7084AF40FA7ABFF8E0C0EFB57A1CC361E18FC4FAC1AB54F3ABFE9FF77263ACE16C3A",
|
||||
"A9391B805CCD956081E0B63D282BEA46E7025126F1C1631239C33E92AA6F92CD56E5A4C56F00FF9658E93D48AF4EF0EF81628E34AD4DB0CDAEDCD2A17EE7"},
|
||||
{"99C0AD703196D2F60A74E6B378B838B31F82EA861F06FC4E",
|
||||
"92745C018AA708ECFEB1667E9F3F1B01",
|
||||
"828C69F376C0C0EC651C67749C69577D589EE39E51404D80EBF70C8660A8F5FD375473F4A7C611D59CB546A605D67446CE2AA844135FCD78BB5FBC90222A00D42920BB1D7EEDFB0C4672554F583EF23184F89063CDECBE482367B5F9AF3ACBC3AF61392BD94CBCD9B64677",
|
||||
"A879214658FD0A5B0E09836639BF82E05EC7A5EF71D4701934BDA228435C68AC3D5CEB54997878B06A655EEACEFB1345C15867E7FE6C6423660C8B88DF128EBD6BCD85118DBAE16E9252FFB204324E5C8F38CA97759BDBF3CB0083",
|
||||
"51FE87996F194A2585E438B023B345439EA60D1AEBED4650CDAF48A4D4EEC4FC77DC71CC4B09D3BEEF8B7B7AF716CE2B4EFFB3AC9E6323C18AC35E0AA6E2BBBC8889490EB6226C896B0D105EAB42BFE7053CCF00ED66BA94C1BA09A792AA873F0C3B26C5C5F9A936E57B25"},
|
||||
{"7086816D00D648FB8304AA8C9E552E1B69A9955FB59B25D1",
|
||||
"0F45CF7F0BF31CCEB85D9DA10F4D749F",
|
||||
"93F27C60A417D9F0669E86ACC784FC8917B502DAF30A6338F11B30B94D74FEFE2F8BE1BBE2EAD10FAB7EED3C6F72B7C3ECEE1937C32ED4970A6404E139209C05",
|
||||
"877F046601F3CBE4FB1491943FA29487E738F94B99AF206262A1D6FF856C9AA0B8D4D08A54370C98F8E88FA3DCC2B14C1F76D71B2A4C7963AEE8AF960464C5BEC8357AD00DC8",
|
||||
"FE96906B895CE6A8E72BC72344E2C8BB3C63113D70EAFA26C299BAFE77A8A6568172EB447FB3E86648A0AF3512DEB1AAC0819F3EC553903BF28A9FB0F43411237A774BF9EE03E445D280FBB9CD12B9BAAB6EF5E52691"},
|
||||
{"062F65A896D5BF1401BADFF70E91B458E1F9BD4888CB2E4D",
|
||||
"5B11EA1D6008EBB41CF892FCA5B943D1",
|
||||
"BAF4FF5C8242",
|
||||
"A8870E091238355984EB2F7D61A865B9170F440BFF999A5993DD41A10F4440D21FF948DDA2BF663B2E03AC3324492DC5E40262ECC6A65C07672353BE23E7FB3A9D79FF6AA38D97960905A38DECC312CB6A59E5467ECF06C311CD43ADC0B543EDF34FE8BE611F176460D5627CA51F8F8D9FED71F55C",
|
||||
"B10E127A632172CF8AA7539B140D2C9C2590E6F28C3CB892FC498FCE56A34F732FBFF32E79C7B9747D9094E8635A0C084D6F0247F9768FB5FF83493799A9BEC6C39572120C40E9292C8C947AE8573462A9108C36D9D7112E6995AE5867E6C8BB387D1C5D4BEF524F391B9FD9F0A3B4BFA079E915BCD920185CFD38D114C558928BD7D47877"},
|
||||
{"38A8E45D6D705A11AF58AED5A1344896998EACF359F2E26A",
|
||||
"FD82B5B31804FF47D44199B533D0CF84",
|
||||
"DE454D4E62FE879F2050EE3E25853623D3E9AC52EEC1A1779A48CFAF5ECA0BFDE44749391866D1",
|
||||
"B804",
|
||||
"164BB965C05EBE0931A1A63293EDF9C38C27"},
|
||||
{"34C33C97C6D7A0850DA94D78A58DC61EC717CD7574833068",
|
||||
"343BE00DA9483F05C14F2E9EB8EA6AE8",
|
||||
"78312A43EFDE3CAE34A65796FF059A3FE15304EEA5CF1D9306949FE5BF3349D4977D4EBE76C040FE894C5949E4E4D6681153DA87FB9AC5062063CA2EA183566343362370944CE0362D25FC195E124FD60E8682E665D13F2229DDA3E4B2CB1DCA",
|
||||
"CC11BB284B1153578E4A5ED9D937B869DAF00F5B1960C23455CA9CC43F486A3BE0B66254F1041F04FDF459C8640465B6E1D2CF899A381451E8E7FCB50CF87823BE77E24B132BBEEDC72E53369B275E1D8F49ECE59F4F215230AC4FE133FC80E4F634EE80BA4682B62C86",
|
||||
"E7F703DC31A95E3A4919FF957836CB76C063D81702AEA4703E1C2BF30831E58C4609D626EC6810E12EAA5B930F049FF9EFC22C3E3F1EBD4A1FB285CB02A1AC5AD46B425199FC0A85670A5C4E3DAA9636C8F64C199F42F18AAC8EA7457FD377F322DD7752D7D01B946C8F0A97E6113F0D50106F319AFD291AAACE"},
|
||||
{"C6ECF7F053573E403E61B83052A343D93CBCC179D1E835BE",
|
||||
"E280E13D7367042E3AA09A80111B6184",
|
||||
"21486C9D7A9647",
|
||||
"5F2639AFA6F17931853791CD8C92382BBB677FD72D0AB1A080D0E49BFAA21810E963E4FACD422E92F65CBFAD5884A60CD94740DF31AF02F95AA57DA0C4401B0ED906",
|
||||
"5C51DB20755302070C45F52E50128A67C8B2E4ED0EACB7E29998CCE2E8C289DD5655913EC1A51CC3AABE5CDC2402B2BE7D6D4BF6945F266FBD70BA9F37109067157AE7530678B45F64475D4EBFCB5FFF46A5"},
|
||||
{"5EC6CF7401BC57B18EF154E8C38ACCA8959E57D2F3975FF5",
|
||||
"656B41CB3F9CF8C08BAD7EBFC80BD225",
|
||||
"6B817C2906E2AF425861A7EF59BA5801F143EE2A139EE72697CDE168B4",
|
||||
"2C0E1DDC9B1E5389BA63845B18B1F8A1DB062037151BCC56EF7C21C0BB4DAE366636BBA975685D7CC5A94AFBE89C769016388C56FB7B57CE750A12B718A8BDCF70E80E8659A8330EFC8F86640F21735E8C80E23FE43ABF23507CE3F964AE4EC99D",
|
||||
"ED780CF911E6D1AA8C979B889B0B9DC1ABE261832980BDBFB576901D9EF5AB8048998E31A15BE54B3E5845A4D136AD24D0BDA1C3006168DF2F8AC06729CB0818867398150020131D8F04EDF1923758C9EABB5F735DE5EA1758D4BC0ACFCA98AFD202E9839B8720253693B874C65586C6F0"},
|
||||
{"C92F678EB2208662F5BCF3403EC05F5961E957908A3E79421E1D25FC19054153",
|
||||
"DA0F3A40983D92F2D4C01FED33C7A192",
|
||||
"2B6E9D26DB406A0FAB47608657AA10EFC2B4AA5F459B29FF85AC9A40BFFE7AEB04F77E9A11FAAA116D7F6D4DA417671A9AB02C588E0EF59CB1BFB4B1CC931B63A3B3A159FCEC97A04D1E6F0C7E6A9CEF6B0ABB04758A69F1FE754DF4C2610E8C46B6CF413BDB31351D55BEDCB7B4A13A1C98E10984475E0F2F957853",
|
||||
"F37326A80E08",
|
||||
"83519E53E321D334F7C10B568183775C0E9AAE55F806"},
|
||||
{"6847E0491BE57E72995D186D50094B0B3593957A5146798FCE68B287B2FB37B5",
|
||||
"3EE1182AEBB19A02B128F28E1D5F7F99",
|
||||
"D9F35ABB16D776CE",
|
||||
"DB7566ED8EA95BDF837F23DB277BAFBC5E70D1105ADFD0D9EF15475051B1EF94709C67DCA9F8D5",
|
||||
"2CDCED0C9EBD6E2A508822A685F7DCD1CDD99E7A5FCA786C234E7F7F1D27EC49751AD5DCFA30C5EDA87C43CAE3B919B6BBCFE34C8EDA59"},
|
||||
{"82B019673642C08388D3E42075A4D5D587558C229E4AB8F660E37650C4C41A0A",
|
||||
"336F5D681E0410FAE7B607246092C6DC",
|
||||
"D430CBD8FE435B64214E9E9CDC5DE99D31CFCFB8C10AA0587A49DF276611",
|
||||
"998404153AD77003E1737EDE93ED79859EE6DCCA93CB40C4363AA817ABF2DBBD46E42A14A7183B6CC01E12A577888141363D0AE011EB6E8D28C0B235",
|
||||
"9BEF69EEB60BD3D6065707B7557F25292A8872857CFBD24F2F3C088E4450995333088DA50FD9121221C504DF1D0CD5EFE6A12666C5D5BB12282CF4C19906E9CFAB97E9BDF7F49DC17CFC384B"},
|
||||
{"747B2E269B1859F0622C15C8BAD6A725028B1F94B8DB7326948D1E6ED663A8BC",
|
||||
"AB91F7245DDCE3F1C747872D47BE0A8A",
|
||||
"3B03F786EF1DDD76E1D42646DA4CD2A5165DC5383CE86D1A0B5F13F910DC278A4E451EE0192CBA178E13B3BA27FDC7840DF73D2E104B",
|
||||
"6B803F4701114F3E5FE21718845F8416F70F626303F545BE197189E0A2BA396F37CE06D389EB2658BC7D56D67868708F6D0D32",
|
||||
"1570DDB0BCE75AA25D1957A287A2C36B1A5F2270186DA81BA6112B7F43B0F3D1D0ED072591DCF1F1C99BBB25621FC39B896FF9BD9413A2845363A9DCD310C32CF98E57"},
|
||||
{"02E59853FB29AEDA0FE1C5F19180AD99A12FF2F144670BB2B8BADF09AD812E0A",
|
||||
"C691294EF67CD04D1B9242AF83DD1421",
|
||||
"879334DAE3",
|
||||
"1E17F46A98FEF5CBB40759D95354",
|
||||
"FED8C3FF27DDF6313AED444A2985B36CBA268AAD6AAC563C0BA28F6DB5DB"},
|
||||
{"F6C1FB9B4188F2288FF03BD716023198C3582CF2A037FC2F29760916C2B7FCDB",
|
||||
"4228DA0678CA3534588859E77DFF014C",
|
||||
"D8153CAF35539A61DD8D05B3C9B44F01E564FB9348BCD09A1C23B84195171308861058F0A3CD2A55B912A3AAEE06FF4D356C77275828F2157C2FC7C115DA39E443210CCC56BEDB0CC99BBFB227ABD5CC454F4E7F547C7378A659EEB6A7E809101A84F866503CB18D4484E1FA09B3EC7FC75EB2E35270800AA7",
|
||||
"23B660A779AD285704B12EC1C580387A47BEC7B00D452C6570",
|
||||
"5AA642BBABA8E49849002A2FAF31DB8FC7773EFDD656E469CEC19B3206D4174C9A263D0A05484261F6"},
|
||||
{"8FF6086F1FADB9A3FBE245EAC52640C43B39D43F89526BB5A6EBA47710931446",
|
||||
"943188480C99437495958B0AE4831AA9",
|
||||
"AD5CD0BDA426F6EBA23C8EB23DC73FF9FEC173355EDBD6C9344C4C4383F211888F7CE6B29899A6801DF6B38651A7C77150941A",
|
||||
"80CD5EA8D7F81DDF5070B934937912E8F541A5301877528EB41AB60C020968D459960ED8FB73083329841A",
|
||||
"ABAE8EB7F36FCA2362551E72DAC890BA1BB6794797E0FC3B67426EC9372726ED4725D379EA0AC9147E48DCD0005C502863C2C5358A38817C8264B5"},
|
||||
{"A083B54E6B1FE01B65D42FCD248F97BB477A41462BBFE6FD591006C022C8FD84",
|
||||
"B0490F5BD68A52459556B3749ACDF40E",
|
||||
"8892E047DA5CFBBDF7F3CFCBD1BD21C6D4C80774B1826999234394BD3E513CC7C222BB40E1E3140A152F19B3802F0D036C24A590512AD0E8",
|
||||
"D7B15752789DC94ED0F36778A5C7BBB207BEC32BAC66E702B39966F06E381E090C6757653C3D26A81EC6AD6C364D66867A334C91BB0B8A8A4B6EACDF0783D09010AEBA2DD2062308FE99CC1F",
|
||||
"C071280A732ADC93DF272BF1E613B2BB7D46FC6665EF2DC1671F3E211D6BDE1D6ADDD28DF3AA2E47053FC8BB8AE9271EC8BC8B2CFFA320D225B451685B6D23ACEFDD241FE284F8ADC8DB07F456985B14330BBB66E0FB212213E05B3E"},
|
||||
}
|
92
vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go
generated
vendored
92
vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.go
generated
vendored
|
@ -1,92 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
// This file contains necessary tools for the aex and ocb packages.
|
||||
//
|
||||
// These functions SHOULD NOT be used elsewhere, since they are optimized for
|
||||
// specific input nature in the EAX and OCB modes of operation.
|
||||
|
||||
package byteutil
|
||||
|
||||
// GfnDouble computes 2 * input in the field of 2^n elements.
|
||||
// The irreducible polynomial in the finite field for n=128 is
|
||||
// x^128 + x^7 + x^2 + x + 1 (equals 0x87)
|
||||
// Constant-time execution in order to avoid side-channel attacks
|
||||
func GfnDouble(input []byte) []byte {
|
||||
if len(input) != 16 {
|
||||
panic("Doubling in GFn only implemented for n = 128")
|
||||
}
|
||||
// If the first bit is zero, return 2L = L << 1
|
||||
// Else return (L << 1) xor 0^120 10000111
|
||||
shifted := ShiftBytesLeft(input)
|
||||
shifted[15] ^= ((input[0] >> 7) * 0x87)
|
||||
return shifted
|
||||
}
|
||||
|
||||
// ShiftBytesLeft outputs the byte array corresponding to x << 1 in binary.
|
||||
func ShiftBytesLeft(x []byte) []byte {
|
||||
l := len(x)
|
||||
dst := make([]byte, l)
|
||||
for i := 0; i < l-1; i++ {
|
||||
dst[i] = (x[i] << 1) | (x[i+1] >> 7)
|
||||
}
|
||||
dst[l-1] = x[l-1] << 1
|
||||
return dst
|
||||
}
|
||||
|
||||
// ShiftNBytesLeft puts in dst the byte array corresponding to x << n in binary.
|
||||
func ShiftNBytesLeft(dst, x []byte, n int) {
|
||||
// Erase first n / 8 bytes
|
||||
copy(dst, x[n/8:])
|
||||
|
||||
// Shift the remaining n % 8 bits
|
||||
bits := uint(n % 8)
|
||||
l := len(dst)
|
||||
for i := 0; i < l-1; i++ {
|
||||
dst[i] = (dst[i] << bits) | (dst[i+1] >> uint(8 - bits))
|
||||
}
|
||||
dst[l-1] = dst[l-1] << bits
|
||||
|
||||
// Append trailing zeroes
|
||||
dst = append(dst, make([]byte, n/8)...)
|
||||
}
|
||||
|
||||
// XorBytesMut assumes equal input length, replaces X with X XOR Y
|
||||
func XorBytesMut(X, Y []byte) {
|
||||
for i := 0; i < len(X); i++ {
|
||||
X[i] ^= Y[i]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// XorBytes assumes equal input length, puts X XOR Y into Z
|
||||
func XorBytes(Z, X, Y []byte) {
|
||||
for i := 0; i < len(X); i++ {
|
||||
Z[i] = X[i] ^ Y[i]
|
||||
}
|
||||
}
|
||||
|
||||
// RightXor XORs smaller input (assumed Y) at the right of the larger input (assumed X)
|
||||
func RightXor(X, Y []byte) []byte {
|
||||
offset := len(X) - len(Y)
|
||||
xored := make([]byte, len(X));
|
||||
copy(xored, X)
|
||||
for i := 0; i < len(Y); i++ {
|
||||
xored[offset + i] ^= Y[i]
|
||||
}
|
||||
return xored
|
||||
}
|
||||
|
||||
// SliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func SliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
317
vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go
generated
vendored
317
vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go
generated
vendored
|
@ -1,317 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
// Package ocb provides an implementation of the OCB (offset codebook) mode of
|
||||
// operation, as described in RFC-7253 of the IRTF and in Rogaway, Bellare,
|
||||
// Black and Krovetz - OCB: A BLOCK-CIPHER MODE OF OPERATION FOR EFFICIENT
|
||||
// AUTHENTICATED ENCRYPTION (2003).
|
||||
// Security considerations (from RFC-7253): A private key MUST NOT be used to
|
||||
// encrypt more than 2^48 blocks. Tag length should be at least 12 bytes (a
|
||||
// brute-force forging adversary succeeds after 2^{tag length} attempts). A
|
||||
// single key SHOULD NOT be used to decrypt ciphertext with different tag
|
||||
// lengths. Nonces need not be secret, but MUST NOT be reused.
|
||||
// This package only supports underlying block ciphers with 128-bit blocks,
|
||||
// such as AES-{128, 192, 256}, but may be extended to other sizes.
|
||||
package ocb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"github.com/ProtonMail/go-crypto/internal/byteutil"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type ocb struct {
|
||||
block cipher.Block
|
||||
tagSize int
|
||||
nonceSize int
|
||||
mask mask
|
||||
// Optimized en/decrypt: For each nonce N used to en/decrypt, the 'Ktop'
|
||||
// internal variable can be reused for en/decrypting with nonces sharing
|
||||
// all but the last 6 bits with N. The prefix of the first nonce used to
|
||||
// compute the new Ktop, and the Ktop value itself, are stored in
|
||||
// reusableKtop. If using incremental nonces, this saves one block cipher
|
||||
// call every 63 out of 64 OCB encryptions, and stores one nonce and one
|
||||
// output of the block cipher in memory only.
|
||||
reusableKtop reusableKtop
|
||||
}
|
||||
|
||||
type mask struct {
|
||||
// L_*, L_$, (L_i)_{i ∈ N}
|
||||
lAst []byte
|
||||
lDol []byte
|
||||
L [][]byte
|
||||
}
|
||||
|
||||
type reusableKtop struct {
|
||||
noncePrefix []byte
|
||||
Ktop []byte
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTagSize = 16
|
||||
defaultNonceSize = 15
|
||||
)
|
||||
|
||||
const (
|
||||
enc = iota
|
||||
dec
|
||||
)
|
||||
|
||||
func (o *ocb) NonceSize() int {
|
||||
return o.nonceSize
|
||||
}
|
||||
|
||||
func (o *ocb) Overhead() int {
|
||||
return o.tagSize
|
||||
}
|
||||
|
||||
// NewOCB returns an OCB instance with the given block cipher and default
|
||||
// tag and nonce sizes.
|
||||
func NewOCB(block cipher.Block) (cipher.AEAD, error) {
|
||||
return NewOCBWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
|
||||
}
|
||||
|
||||
// NewOCBWithNonceAndTagSize returns an OCB instance with the given block
|
||||
// cipher, nonce length, and tag length. Panics on zero nonceSize and
|
||||
// exceedingly long tag size.
|
||||
//
|
||||
// It is recommended to use at least 12 bytes as tag length.
|
||||
func NewOCBWithNonceAndTagSize(
|
||||
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
|
||||
if block.BlockSize() != 16 {
|
||||
return nil, ocbError("Block cipher must have 128-bit blocks")
|
||||
}
|
||||
if nonceSize < 1 {
|
||||
return nil, ocbError("Incorrect nonce length")
|
||||
}
|
||||
if nonceSize >= block.BlockSize() {
|
||||
return nil, ocbError("Nonce length exceeds blocksize - 1")
|
||||
}
|
||||
if tagSize > block.BlockSize() {
|
||||
return nil, ocbError("Custom tag length exceeds blocksize")
|
||||
}
|
||||
return &ocb{
|
||||
block: block,
|
||||
tagSize: tagSize,
|
||||
nonceSize: nonceSize,
|
||||
mask: initializeMaskTable(block),
|
||||
reusableKtop: reusableKtop{
|
||||
noncePrefix: nil,
|
||||
Ktop: nil,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *ocb) Seal(dst, nonce, plaintext, adata []byte) []byte {
|
||||
if len(nonce) > o.nonceSize {
|
||||
panic("crypto/ocb: Incorrect nonce length given to OCB")
|
||||
}
|
||||
ret, out := byteutil.SliceForAppend(dst, len(plaintext)+o.tagSize)
|
||||
o.crypt(enc, out, nonce, adata, plaintext)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (o *ocb) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
|
||||
if len(nonce) > o.nonceSize {
|
||||
panic("Nonce too long for this instance")
|
||||
}
|
||||
if len(ciphertext) < o.tagSize {
|
||||
return nil, ocbError("Ciphertext shorter than tag length")
|
||||
}
|
||||
sep := len(ciphertext) - o.tagSize
|
||||
ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
|
||||
ciphertextData := ciphertext[:sep]
|
||||
tag := ciphertext[sep:]
|
||||
o.crypt(dec, out, nonce, adata, ciphertextData)
|
||||
if subtle.ConstantTimeCompare(ret[sep:], tag) == 1 {
|
||||
ret = ret[:sep]
|
||||
return ret, nil
|
||||
}
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
return nil, ocbError("Tag authentication failed")
|
||||
}
|
||||
|
||||
// On instruction enc (resp. dec), crypt is the encrypt (resp. decrypt)
|
||||
// function. It returns the resulting plain/ciphertext with the tag appended.
|
||||
func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte {
|
||||
//
|
||||
// Consider X as a sequence of 128-bit blocks
|
||||
//
|
||||
// Note: For encryption (resp. decryption), X is the plaintext (resp., the
|
||||
// ciphertext without the tag).
|
||||
blockSize := o.block.BlockSize()
|
||||
|
||||
//
|
||||
// Nonce-dependent and per-encryption variables
|
||||
//
|
||||
// Zero out the last 6 bits of the nonce into truncatedNonce to see if Ktop
|
||||
// is already computed.
|
||||
truncatedNonce := make([]byte, len(nonce))
|
||||
copy(truncatedNonce, nonce)
|
||||
truncatedNonce[len(truncatedNonce)-1] &= 192
|
||||
Ktop := make([]byte, blockSize)
|
||||
if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) {
|
||||
Ktop = o.reusableKtop.Ktop
|
||||
} else {
|
||||
// Nonce = num2str(TAGLEN mod 128, 7) || zeros(120 - bitlen(N)) || 1 || N
|
||||
paddedNonce := append(make([]byte, blockSize-1-len(nonce)), 1)
|
||||
paddedNonce = append(paddedNonce, truncatedNonce...)
|
||||
paddedNonce[0] |= byte(((8 * o.tagSize) % (8 * blockSize)) << 1)
|
||||
// Last 6 bits of paddedNonce are already zero. Encrypt into Ktop
|
||||
paddedNonce[blockSize-1] &= 192
|
||||
Ktop = paddedNonce
|
||||
o.block.Encrypt(Ktop, Ktop)
|
||||
o.reusableKtop.noncePrefix = truncatedNonce
|
||||
o.reusableKtop.Ktop = Ktop
|
||||
}
|
||||
|
||||
// Stretch = Ktop || ((lower half of Ktop) XOR (lower half of Ktop << 8))
|
||||
xorHalves := make([]byte, blockSize/2)
|
||||
byteutil.XorBytes(xorHalves, Ktop[:blockSize/2], Ktop[1:1+blockSize/2])
|
||||
stretch := append(Ktop, xorHalves...)
|
||||
bottom := int(nonce[len(nonce)-1] & 63)
|
||||
offset := make([]byte, len(stretch))
|
||||
byteutil.ShiftNBytesLeft(offset, stretch, bottom)
|
||||
offset = offset[:blockSize]
|
||||
|
||||
//
|
||||
// Process any whole blocks
|
||||
//
|
||||
// Note: For encryption Y is ciphertext || tag, for decryption Y is
|
||||
// plaintext || tag.
|
||||
checksum := make([]byte, blockSize)
|
||||
m := len(X) / blockSize
|
||||
for i := 0; i < m; i++ {
|
||||
index := bits.TrailingZeros(uint(i + 1))
|
||||
if len(o.mask.L)-1 < index {
|
||||
o.mask.extendTable(index)
|
||||
}
|
||||
byteutil.XorBytesMut(offset, o.mask.L[bits.TrailingZeros(uint(i+1))])
|
||||
blockX := X[i*blockSize : (i+1)*blockSize]
|
||||
blockY := Y[i*blockSize : (i+1)*blockSize]
|
||||
byteutil.XorBytes(blockY, blockX, offset)
|
||||
switch instruction {
|
||||
case enc:
|
||||
o.block.Encrypt(blockY, blockY)
|
||||
byteutil.XorBytesMut(blockY, offset)
|
||||
byteutil.XorBytesMut(checksum, blockX)
|
||||
case dec:
|
||||
o.block.Decrypt(blockY, blockY)
|
||||
byteutil.XorBytesMut(blockY, offset)
|
||||
byteutil.XorBytesMut(checksum, blockY)
|
||||
}
|
||||
}
|
||||
//
|
||||
// Process any final partial block and compute raw tag
|
||||
//
|
||||
tag := make([]byte, blockSize)
|
||||
if len(X)%blockSize != 0 {
|
||||
byteutil.XorBytesMut(offset, o.mask.lAst)
|
||||
pad := make([]byte, blockSize)
|
||||
o.block.Encrypt(pad, offset)
|
||||
chunkX := X[blockSize*m:]
|
||||
chunkY := Y[blockSize*m : len(X)]
|
||||
byteutil.XorBytes(chunkY, chunkX, pad[:len(chunkX)])
|
||||
// P_* || bit(1) || zeroes(127) - len(P_*)
|
||||
switch instruction {
|
||||
case enc:
|
||||
paddedY := append(chunkX, byte(128))
|
||||
paddedY = append(paddedY, make([]byte, blockSize-len(chunkX)-1)...)
|
||||
byteutil.XorBytesMut(checksum, paddedY)
|
||||
case dec:
|
||||
paddedX := append(chunkY, byte(128))
|
||||
paddedX = append(paddedX, make([]byte, blockSize-len(chunkY)-1)...)
|
||||
byteutil.XorBytesMut(checksum, paddedX)
|
||||
}
|
||||
byteutil.XorBytes(tag, checksum, offset)
|
||||
byteutil.XorBytesMut(tag, o.mask.lDol)
|
||||
o.block.Encrypt(tag, tag)
|
||||
byteutil.XorBytesMut(tag, o.hash(adata))
|
||||
copy(Y[blockSize*m+len(chunkY):], tag[:o.tagSize])
|
||||
} else {
|
||||
byteutil.XorBytes(tag, checksum, offset)
|
||||
byteutil.XorBytesMut(tag, o.mask.lDol)
|
||||
o.block.Encrypt(tag, tag)
|
||||
byteutil.XorBytesMut(tag, o.hash(adata))
|
||||
copy(Y[blockSize*m:], tag[:o.tagSize])
|
||||
}
|
||||
return Y
|
||||
}
|
||||
|
||||
// This hash function is used to compute the tag. Per design, on empty input it
|
||||
// returns a slice of zeros, of the same length as the underlying block cipher
|
||||
// block size.
|
||||
func (o *ocb) hash(adata []byte) []byte {
|
||||
//
|
||||
// Consider A as a sequence of 128-bit blocks
|
||||
//
|
||||
A := make([]byte, len(adata))
|
||||
copy(A, adata)
|
||||
blockSize := o.block.BlockSize()
|
||||
|
||||
//
|
||||
// Process any whole blocks
|
||||
//
|
||||
sum := make([]byte, blockSize)
|
||||
offset := make([]byte, blockSize)
|
||||
m := len(A) / blockSize
|
||||
for i := 0; i < m; i++ {
|
||||
chunk := A[blockSize*i : blockSize*(i+1)]
|
||||
index := bits.TrailingZeros(uint(i + 1))
|
||||
// If the mask table is too short
|
||||
if len(o.mask.L)-1 < index {
|
||||
o.mask.extendTable(index)
|
||||
}
|
||||
byteutil.XorBytesMut(offset, o.mask.L[index])
|
||||
byteutil.XorBytesMut(chunk, offset)
|
||||
o.block.Encrypt(chunk, chunk)
|
||||
byteutil.XorBytesMut(sum, chunk)
|
||||
}
|
||||
|
||||
//
|
||||
// Process any final partial block; compute final hash value
|
||||
//
|
||||
if len(A)%blockSize != 0 {
|
||||
byteutil.XorBytesMut(offset, o.mask.lAst)
|
||||
// Pad block with 1 || 0 ^ 127 - bitlength(a)
|
||||
ending := make([]byte, blockSize-len(A)%blockSize)
|
||||
ending[0] = 0x80
|
||||
encrypted := append(A[blockSize*m:], ending...)
|
||||
byteutil.XorBytesMut(encrypted, offset)
|
||||
o.block.Encrypt(encrypted, encrypted)
|
||||
byteutil.XorBytesMut(sum, encrypted)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func initializeMaskTable(block cipher.Block) mask {
|
||||
//
|
||||
// Key-dependent variables
|
||||
//
|
||||
lAst := make([]byte, block.BlockSize())
|
||||
block.Encrypt(lAst, lAst)
|
||||
lDol := byteutil.GfnDouble(lAst)
|
||||
L := make([][]byte, 1)
|
||||
L[0] = byteutil.GfnDouble(lDol)
|
||||
|
||||
return mask{
|
||||
lAst: lAst,
|
||||
lDol: lDol,
|
||||
L: L,
|
||||
}
|
||||
}
|
||||
|
||||
// Extends the L array of mask m up to L[limit], with L[i] = GfnDouble(L[i-1])
|
||||
func (m *mask) extendTable(limit int) {
|
||||
for i := len(m.L); i <= limit; i++ {
|
||||
m.L = append(m.L, byteutil.GfnDouble(m.L[i-1]))
|
||||
}
|
||||
}
|
||||
|
||||
func ocbError(err string) error {
|
||||
return errors.New("crypto/ocb: " + err)
|
||||
}
|
136
vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.go
generated
vendored
136
vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.go
generated
vendored
|
@ -1,136 +0,0 @@
|
|||
// In the test vectors provided by RFC 7253, the "bottom"
|
||||
// internal variable, which defines "offset" for the first time, does not
|
||||
// exceed 15. However, it can attain values up to 63.
|
||||
|
||||
// These vectors include key length in {128, 192, 256}, tag size 128, and
|
||||
// random nonce, header, and plaintext lengths.
|
||||
|
||||
// This file was automatically generated.
|
||||
|
||||
package ocb
|
||||
|
||||
var randomVectors = []struct {
|
||||
key, nonce, header, plaintext, ciphertext string
|
||||
}{
|
||||
|
||||
{"9438C5D599308EAF13F800D2D31EA7F0",
|
||||
"C38EE4801BEBFFA1CD8635BE",
|
||||
"0E507B7DADD8A98CDFE272D3CB6B3E8332B56AE583FB049C0874D4200BED16BD1A044182434E9DA0E841F182DFD5B3016B34641CED0784F1745F63AB3D0DA22D3351C9EF9A658B8081E24498EBF61FCE40DA6D8E184536",
|
||||
"962D227786FB8913A8BAD5DC3250",
|
||||
"EEDEF5FFA5986D1E3BF86DDD33EF9ADC79DCA06E215FA772CCBA814F63AD"},
|
||||
{"BA7DE631C7D6712167C6724F5B9A2B1D",
|
||||
"35263EBDA05765DC0E71F1F5",
|
||||
"0103257B4224507C0242FEFE821EA7FA42E0A82863E5F8B68F7D881B4B44FA428A2B6B21D2F591260802D8AB6D83",
|
||||
"9D6D1FC93AE8A64E7889B7B2E3521EFA9B920A8DDB692E6F833DDC4A38AFA535E5E2A3ED82CB7E26404AB86C54D01C4668F28398C2DF33D5D561CBA1C8DCFA7A912F5048E545B59483C0E3221F54B14DAA2E4EB657B3BEF9554F34CAD69B2724AE962D3D8A",
|
||||
"E93852D1985C5E775655E937FA79CE5BF28A585F2AF53A5018853B9634BE3C84499AC0081918FDCE0624494D60E25F76ACD6853AC7576E3C350F332249BFCABD4E73CEABC36BE4EDDA40914E598AE74174A0D7442149B26990899491BDDFE8FC54D6C18E83AE9E9A6FFBF5D376565633862EEAD88D"},
|
||||
{"2E74B25289F6FD3E578C24866E9C72A5",
|
||||
"FD912F15025AF8414642BA1D1D",
|
||||
"FB5FB8C26F365EEDAB5FE260C6E3CCD27806729C8335F146063A7F9EA93290E56CF84576EB446350D22AD730547C267B1F0BBB97EB34E1E2C41A",
|
||||
"6C092EBF78F76EE8C1C6E592277D9545BA16EDB67BC7D8480B9827702DC2F8A129E2B08A2CE710CA7E1DA45CE162BB6CD4B512E632116E2211D3C90871EFB06B8D4B902681C7FB",
|
||||
"6AC0A77F26531BF4F354A1737F99E49BE32ECD909A7A71AD69352906F54B08A9CE9B8CA5D724CBFFC5673437F23F630697F3B84117A1431D6FA8CC13A974FB4AD360300522E09511B99E71065D5AC4BBCB1D791E864EF4"},
|
||||
{"E7EC507C802528F790AFF5303A017B17",
|
||||
"4B97A7A568940A9E3CE7A99E93031E",
|
||||
"28349BDC5A09390C480F9B8AA3EDEA3DDB8B9D64BCA322C570B8225DF0E31190DAB25A4014BA39519E02ABFB12B89AA28BBFD29E486E7FB28734258C817B63CED9912DBAFEBB93E2798AB2890DE3B0ACFCFF906AB15563EF7823CE83D27CDB251195E22BD1337BCBDE65E7C2C427321C463C2777BFE5AEAA",
|
||||
"9455B3EA706B74",
|
||||
"7F33BA3EA848D48A96B9530E26888F43EBD4463C9399B6"},
|
||||
{"6C928AA3224736F28EE7378DE0090191",
|
||||
"8936138E2E4C6A13280017A1622D",
|
||||
"6202717F2631565BDCDC57C6584543E72A7C8BD444D0D108ED35069819633C",
|
||||
"DA0691439E5F035F3E455269D14FE5C201C8C9B0A3FE2D3F86BCC59387C868FE65733D388360B31E3CE28B4BF6A8BE636706B536D5720DB66B47CF1C7A5AFD6F61E0EF90F1726D6B0E169F9A768B2B7AE4EE00A17F630AC905FCAAA1B707FFF25B3A1AAE83B504837C64A5639B2A34002B300EC035C9B43654DA55",
|
||||
"B8804D182AB0F0EEB464FA7BD1329AD6154F982013F3765FEDFE09E26DAC078C9C1439BFC1159D6C02A25E3FF83EF852570117B315852AD5EE20E0FA3AA0A626B0E43BC0CEA38B44579DD36803455FB46989B90E6D229F513FD727AF8372517E9488384C515D6067704119C931299A0982EDDFB9C2E86A90C450C077EB222511EC9CCABC9FCFDB19F70088"},
|
||||
{"ECEA315CA4B3F425B0C9957A17805EA4",
|
||||
"664CDAE18403F4F9BA13015A44FC",
|
||||
"642AFB090D6C6DB46783F08B01A3EF2A8FEB5736B531EAC226E7888FCC8505F396818F83105065FACB3267485B9E5E4A0261F621041C08FCCB2A809A49AB5252A91D0971BCC620B9D614BD77E57A0EED2FA5",
|
||||
"6852C31F8083E20E364CEA21BB7854D67CEE812FE1C9ED2425C0932A90D3780728D1BB",
|
||||
"2ECEF962A9695A463ADABB275BDA9FF8B2BA57AEC2F52EFFB700CD9271A74D2A011C24AEA946051BD6291776429B7E681BA33E"},
|
||||
{"4EE616C4A58AAA380878F71A373461F6",
|
||||
"91B8C9C176D9C385E9C47E52",
|
||||
"CDA440B7F9762C572A718AC754EDEECC119E5EE0CCB9FEA4FFB22EEE75087C032EBF3DA9CDD8A28CC010B99ED45143B41A4BA50EA2A005473F89639237838867A57F23B0F0ED3BF22490E4501DAC9C658A9B9F",
|
||||
"D6E645FA9AE410D15B8123FD757FA356A8DBE9258DDB5BE88832E615910993F497EC",
|
||||
"B70ED7BF959FB2AAED4F36174A2A99BFB16992C8CDF369C782C4DB9C73DE78C5DB8E0615F647243B97ACDB24503BC9CADC48"},
|
||||
{"DCD475773136C830D5E3D0C5FE05B7FF",
|
||||
"BB8E1FBB483BE7616A922C4A",
|
||||
"36FEF2E1CB29E76A6EA663FC3AF66ECD7404F466382F7B040AABED62293302B56E8783EF7EBC21B4A16C3E78A7483A0A403F253A2CDC5BBF79DC3DAE6C73F39A961D8FBBE8D41B",
|
||||
"441E886EA38322B2437ECA7DEB5282518865A66780A454E510878E61BFEC3106A3CD93D2A02052E6F9E1832F9791053E3B76BF4C07EFDD6D4106E3027FABB752E60C1AA425416A87D53938163817A1051EBA1D1DEEB4B9B25C7E97368B52E5911A31810B0EC5AF547559B6142D9F4C4A6EF24A4CF75271BF9D48F62B",
|
||||
"1BE4DD2F4E25A6512C2CC71D24BBB07368589A94C2714962CD0ACE5605688F06342587521E75F0ACAFFD86212FB5C34327D238DB36CF2B787794B9A4412E7CD1410EA5DDD2450C265F29CF96013CD213FD2880657694D718558964BC189B4A84AFCF47EB012935483052399DBA5B088B0A0477F20DFE0E85DCB735E21F22A439FB837DD365A93116D063E607"},
|
||||
{"3FBA2B3D30177FFE15C1C59ED2148BB2C091F5615FBA7C07",
|
||||
"FACF804A4BEBF998505FF9DE",
|
||||
"8213B9263B2971A5BDA18DBD02208EE1",
|
||||
"15B323926993B326EA19F892D704439FC478828322AF72118748284A1FD8A6D814E641F70512FD706980337379F31DC63355974738D7FEA87AD2858C0C2EBBFBE74371C21450072373C7B651B334D7C4D43260B9D7CCD3AF9EDB",
|
||||
"6D35DC1469B26E6AAB26272A41B46916397C24C485B61162E640A062D9275BC33DDCFD3D9E1A53B6C8F51AC89B66A41D59B3574197A40D9B6DCF8A4E2A001409C8112F16B9C389E0096179DB914E05D6D11ED0005AD17E1CE105A2F0BAB8F6B1540DEB968B7A5428FF44"},
|
||||
{"53B52B8D4D748BCDF1DDE68857832FA46227FA6E2F32EFA1",
|
||||
"0B0EF53D4606B28D1398355F",
|
||||
"F23882436349094AF98BCACA8218E81581A043B19009E28EFBF2DE37883E04864148CC01D240552CA8844EC1456F42034653067DA67E80F87105FD06E14FF771246C9612867BE4D215F6D761",
|
||||
"F15030679BD4088D42CAC9BF2E9606EAD4798782FA3ED8C57EBE7F84A53236F51B25967C6489D0CD20C9EEA752F9BC",
|
||||
"67B96E2D67C3729C96DAEAEDF821D61C17E648643A2134C5621FEC621186915AD80864BFD1EB5B238BF526A679385E012A457F583AFA78134242E9D9C1B4E4"},
|
||||
{"0272DD80F23399F49BFC320381A5CD8225867245A49A7D41",
|
||||
"5C83F4896D0738E1366B1836",
|
||||
"69B0337289B19F73A12BAEEA857CCAF396C11113715D9500CCCF48BA08CFF12BC8B4BADB3084E63B85719DB5058FA7C2C11DEB096D7943CFA7CAF5",
|
||||
"C01AD10FC8B562CD17C7BC2FAB3E26CBDFF8D7F4DEA816794BBCC12336991712972F52816AABAB244EB43B0137E2BAC1DD413CE79531E78BEF782E6B439612BB3AEF154DE3502784F287958EBC159419F9EBA27916A28D6307324129F506B1DE80C1755A929F87",
|
||||
"FEFE52DD7159C8DD6E8EC2D3D3C0F37AB6CB471A75A071D17EC4ACDD8F3AA4D7D4F7BB559F3C09099E3D9003E5E8AA1F556B79CECDE66F85B08FA5955E6976BF2695EA076388A62D2AD5BAB7CBF1A7F3F4C8D5CDF37CDE99BD3E30B685D9E5EEE48C7C89118EF4878EB89747F28271FA2CC45F8E9E7601"},
|
||||
{"3EEAED04A455D6E5E5AB53CFD5AFD2F2BC625C7BF4BE49A5",
|
||||
"36B88F63ADBB5668588181D774",
|
||||
"D367E3CB3703E762D23C6533188EF7028EFF9D935A3977150361997EC9DEAF1E4794BDE26AA8B53C124980B1362EC86FCDDFC7A90073171C1BAEE351A53234B86C66E8AB92FAE99EC6967A6D3428892D80",
|
||||
"573454C719A9A55E04437BF7CBAAF27563CCCD92ADD5E515CD63305DFF0687E5EEF790C5DCA5C0033E9AB129505E2775438D92B38F08F3B0356BA142C6F694",
|
||||
"E9F79A5B432D9E682C9AAA5661CFC2E49A0FCB81A431E54B42EB73DD3BED3F377FEC556ABA81624BA64A5D739AD41467460088F8D4F442180A9382CA635745473794C382FCDDC49BA4EB6D8A44AE3C"},
|
||||
{"B695C691538F8CBD60F039D0E28894E3693CC7C36D92D79D",
|
||||
"BC099AEB637361BAC536B57618",
|
||||
"BFFF1A65AE38D1DC142C71637319F5F6508E2CB33C9DCB94202B359ED5A5ED8042E7F4F09231D32A7242976677E6F4C549BF65FADC99E5AF43F7A46FD95E16C2",
|
||||
"081DF3FD85B415D803F0BE5AC58CFF0023FDDED99788296C3731D8",
|
||||
"E50C64E3614D94FE69C47092E46ACC9957C6FEA2CCBF96BC62FBABE7424753C75F9C147C42AE26FE171531"},
|
||||
{"C9ACBD2718F0689A1BE9802A551B6B8D9CF5614DAF5E65ED",
|
||||
"B1B0AAF373B8B026EB80422051D8",
|
||||
"6648C0E61AC733C76119D23FB24548D637751387AA2EAE9D80E912B7BD486CAAD9EAF4D7A5FE2B54AAD481E8EC94BB4D558000896E2010462B70C9FED1E7273080D1",
|
||||
"189F591F6CB6D59AFEDD14C341741A8F1037DC0DF00FC57CE65C30F49E860255CEA5DC6019380CC0FE8880BC1A9E685F41C239C38F36E3F2A1388865C5C311059C0A",
|
||||
"922A5E949B61D03BE34AB5F4E58607D4504EA14017BB363DAE3C873059EA7A1C77A746FB78981671D26C2CF6D9F24952D510044CE02A10177E9DB42D0145211DFE6E84369C5E3BC2669EAB4147B2822895F9"},
|
||||
{"7A832BD2CF5BF4919F353CE2A8C86A5E406DA2D52BE16A72",
|
||||
"2F2F17CECF7E5A756D10785A3CB9DB",
|
||||
"61DA05E3788CC2D8405DBA70C7A28E5AF699863C9F72E6C6770126929F5D6FA267F005EBCF49495CB46400958A3AE80D1289D1C671",
|
||||
"44E91121195A41AF14E8CFDBD39A4B517BE0DF1A72977ED8A3EEF8EEDA1166B2EB6DB2C4AE2E74FA0F0C74537F659BFBD141E5DDEC67E64EDA85AABD3F52C85A785B9FB3CECD70E7DF",
|
||||
"BEDF596EA21288D2B84901E188F6EE1468B14D5161D3802DBFE00D60203A24E2AB62714BF272A45551489838C3A7FEAADC177B591836E73684867CCF4E12901DCF2064058726BBA554E84ADC5136F507E961188D4AF06943D3"},
|
||||
{"1508E8AE9079AA15F1CEC4F776B4D11BCCB061B58AA56C18",
|
||||
"BCA625674F41D1E3AB47672DC0C3",
|
||||
"8B12CF84F16360F0EAD2A41BC021530FFCEC7F3579CAE658E10E2D3D81870F65AFCED0C77C6C4C6E6BA424FF23088C796BA6195ABA35094BF1829E089662E7A95FC90750AE16D0C8AFA55DAC789D7735B970B58D4BE7CEC7341DA82A0179A01929C27A59C5063215B859EA43",
|
||||
"E525422519ECE070E82C",
|
||||
"B47BC07C3ED1C0A43BA52C43CBACBCDBB29CAF1001E09FDF7107"},
|
||||
{"7550C2761644E911FE9ADD119BAC07376BEA442845FEAD876D7E7AC1B713E464",
|
||||
"36D2EC25ADD33CDEDF495205BBC923",
|
||||
"7FCFE81A3790DE97FFC3DE160C470847EA7E841177C2F759571CBD837EA004A6CA8C6F4AEBFF2E9FD552D73EB8A30705D58D70C0B67AEEA280CBBF0A477358ACEF1E7508F2735CD9A0E4F9AC92B8C008F575D3B6278F1C18BD01227E3502E5255F3AB1893632AD00C717C588EF652A51A43209E7EE90",
|
||||
"2B1A62F8FDFAA3C16470A21AD307C9A7D03ADE8EF72C69B06F8D738CDE578D7AEFD0D40BD9C022FB9F580DF5394C998ACCCEFC5471A3996FB8F1045A81FDC6F32D13502EA65A211390C8D882B8E0BEFD8DD8CBEF51D1597B124E9F7F",
|
||||
"C873E02A22DB89EB0787DB6A60B99F7E4A0A085D5C4232A81ADCE2D60AA36F92DDC33F93DD8640AC0E08416B187FB382B3EC3EE85A64B0E6EE41C1366A5AD2A282F66605E87031CCBA2FA7B2DA201D975994AADE3DD1EE122AE09604AD489B84BF0C1AB7129EE16C6934850E"},
|
||||
{"A51300285E554FDBDE7F771A9A9A80955639DD87129FAEF74987C91FB9687C71",
|
||||
"81691D5D20EC818FCFF24B33DECC",
|
||||
"C948093218AA9EB2A8E44A87EEA73FC8B6B75A196819A14BD83709EA323E8DF8B491045220E1D88729A38DBCFFB60D3056DAD4564498FD6574F74512945DEB34B69329ACED9FFC05D5D59DFCD5B973E2ACAFE6AD1EF8BBBC49351A2DD12508ED89ED",
|
||||
"EB861165DAF7625F827C6B574ED703F03215",
|
||||
"C6CD1CE76D2B3679C1B5AA1CFD67CCB55444B6BFD3E22C81CBC9BB738796B83E54E3"},
|
||||
{"8CE0156D26FAEB7E0B9B800BBB2E9D4075B5EAC5C62358B0E7F6FCE610223282",
|
||||
"D2A7B94DD12CDACA909D3AD7",
|
||||
"E021A78F374FC271389AB9A3E97077D755",
|
||||
"7C26000B58929F5095E1CEE154F76C2A299248E299F9B5ADE6C403AA1FD4A67FD4E0232F214CE7B919EE7A1027D2B76C57475715CD078461",
|
||||
"C556FB38DF069B56F337B5FF5775CE6EAA16824DFA754F20B78819028EA635C3BB7AA731DE8776B2DCB67DCA2D33EEDF3C7E52EA450013722A41755A0752433ED17BDD5991AAE77A"},
|
||||
{"1E8000A2CE00A561C9920A30BF0D7B983FEF8A1014C8F04C35CA6970E6BA02BD",
|
||||
"65ED3D63F79F90BBFD19775E",
|
||||
"336A8C0B7243582A46B221AA677647FCAE91",
|
||||
"134A8B34824A290E7B",
|
||||
"914FBEF80D0E6E17F8BDBB6097EBF5FBB0554952DC2B9E5151"},
|
||||
{"53D5607BBE690B6E8D8F6D97F3DF2BA853B682597A214B8AA0EA6E598650AF15",
|
||||
"C391A856B9FE234E14BA1AC7BB40FF",
|
||||
"479682BC21349C4BE1641D5E78FE2C79EC1B9CF5470936DCAD9967A4DCD7C4EFADA593BC9EDE71E6A08829B8580901B61E274227E9D918502DE3",
|
||||
"EAD154DC09C5E26C5D26FF33ED148B27120C7F2C23225CC0D0631B03E1F6C6D96FEB88C1A4052ACB4CE746B884B6502931F407021126C6AAB8C514C077A5A38438AE88EE",
|
||||
"938821286EBB671D999B87C032E1D6055392EB564E57970D55E545FC5E8BAB90E6E3E3C0913F6320995FC636D72CD9919657CC38BD51552F4A502D8D1FE56DB33EBAC5092630E69EBB986F0E15CEE9FC8C052501"},
|
||||
{"294362FCC984F440CEA3E9F7D2C06AF20C53AAC1B3738CA2186C914A6E193ABB",
|
||||
"B15B61C8BB39261A8F55AB178EC3",
|
||||
"D0729B6B75BB",
|
||||
"2BD089ADCE9F334BAE3B065996C7D616DD0C27DF4218DCEEA0FBCA0F968837CE26B0876083327E25681FDDD620A32EC0DA12F73FAE826CC94BFF2B90A54D2651",
|
||||
"AC94B25E4E21DE2437B806966CCD5D9385EF0CD4A51AB9FA6DE675C7B8952D67802E9FEC1FDE9F5D1EAB06057498BC0EEA454804FC9D2068982A3E24182D9AC2E7AB9994DDC899A604264583F63D066B"},
|
||||
{"959DBFEB039B1A5B8CE6A44649B602AAA5F98A906DB96143D202CD2024F749D9",
|
||||
"01D7BDB1133E9C347486C1EFA6",
|
||||
"F3843955BD741F379DD750585EDC55E2CDA05CCBA8C1F4622AC2FE35214BC3A019B8BD12C4CC42D9213D1E1556941E8D8450830287FFB3B763A13722DD4140ED9846FB5FFF745D7B0B967D810A068222E10B259AF1D392035B0D83DC1498A6830B11B2418A840212599171E0258A1C203B05362978",
|
||||
"A21811232C950FA8B12237C2EBD6A7CD2C3A155905E9E0C7C120",
|
||||
"63C1CE397B22F1A03F1FA549B43178BC405B152D3C95E977426D519B3DFCA28498823240592B6EEE7A14"},
|
||||
{"096AE499F5294173F34FF2B375F0E5D5AB79D0D03B33B1A74D7D576826345DF4",
|
||||
"0C52B3D11D636E5910A4DD76D32C",
|
||||
"229E9ECA3053789E937447BC719467075B6138A142DA528DA8F0CF8DDF022FD9AF8E74779BA3AC306609",
|
||||
"8B7A00038783E8BAF6EDEAE0C4EAB48FC8FD501A588C7E4A4DB71E3604F2155A97687D3D2FFF8569261375A513CF4398CE0F87CA1658A1050F6EF6C4EA3E25",
|
||||
"C20B6CF8D3C8241825FD90B2EDAC7593600646E579A8D8DAAE9E2E40C3835FE801B2BE4379131452BC5182C90307B176DFBE2049544222FE7783147B690774F6D9D7CEF52A91E61E298E9AA15464AC"},
|
||||
}
|
78
vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.go
generated
vendored
78
vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.go
generated
vendored
|
@ -1,78 +0,0 @@
|
|||
package ocb
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// Test vectors from https://tools.ietf.org/html/rfc7253. Note that key is
|
||||
// shared accross tests.
|
||||
var testKey, _ = hex.DecodeString("000102030405060708090A0B0C0D0E0F")
|
||||
|
||||
var rfc7253testVectors = []struct {
|
||||
nonce, header, plaintext, ciphertext string
|
||||
}{
|
||||
{"BBAA99887766554433221100",
|
||||
"",
|
||||
"",
|
||||
"785407BFFFC8AD9EDCC5520AC9111EE6"},
|
||||
{"BBAA99887766554433221101",
|
||||
"0001020304050607",
|
||||
"0001020304050607",
|
||||
"6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"},
|
||||
{"BBAA99887766554433221102",
|
||||
"0001020304050607",
|
||||
"",
|
||||
"81017F8203F081277152FADE694A0A00"},
|
||||
{"BBAA99887766554433221103",
|
||||
"",
|
||||
"0001020304050607",
|
||||
"45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"},
|
||||
{"BBAA99887766554433221104",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358"},
|
||||
{"BBAA99887766554433221105",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"",
|
||||
"8CF761B6902EF764462AD86498CA6B97"},
|
||||
{"BBAA99887766554433221106",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F",
|
||||
"5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D"},
|
||||
{"BBAA99887766554433221107",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F"},
|
||||
{"BBAA99887766554433221108",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"",
|
||||
"6DC225A071FC1B9F7C69F93B0F1E10DE"},
|
||||
{"BBAA99887766554433221109",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||
"221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF"},
|
||||
{"BBAA9988776655443322110A",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"},
|
||||
{"BBAA9988776655443322110B",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"",
|
||||
"FE80690BEE8A485D11F32965BC9D2A32"},
|
||||
{"BBAA9988776655443322110C",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"},
|
||||
{"BBAA9988776655443322110D",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60"},
|
||||
{"BBAA9988776655443322110E",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"",
|
||||
"C5CD9D1850C141E358649994EE701B68"},
|
||||
{"BBAA9988776655443322110F",
|
||||
"",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479"},
|
||||
}
|
24
vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.go
generated
vendored
24
vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.go
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
package ocb
|
||||
|
||||
// Second set of test vectors from https://tools.ietf.org/html/rfc7253
|
||||
var rfc7253TestVectorTaglen96 = struct {
|
||||
key, nonce, header, plaintext, ciphertext string
|
||||
}{"0F0E0D0C0B0A09080706050403020100",
|
||||
"BBAA9988776655443322110D",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
||||
"1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA"}
|
||||
|
||||
var rfc7253AlgorithmTest = []struct {
|
||||
KEYLEN, TAGLEN int
|
||||
OUTPUT string }{
|
||||
{128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"},
|
||||
{192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"},
|
||||
{256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"},
|
||||
{128, 96, "77A3D8E73589158D25D01209"},
|
||||
{192, 96, "05D56EAD2752C86BE6932C5E"},
|
||||
{256, 96, "5458359AC23B0CBA9E6330DD"},
|
||||
{128, 64, "192C9B7BD90BA06A"},
|
||||
{192, 64, "0066BC6E0EF34E24"},
|
||||
{256, 64, "7D4EA5D445501CBE"},
|
||||
}
|
153
vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go
generated
vendored
153
vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.go
generated
vendored
|
@ -1,153 +0,0 @@
|
|||
// Copyright 2014 Matthew Endsley
|
||||
// All rights reserved
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted providing that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package keywrap is an implementation of the RFC 3394 AES key wrapping
|
||||
// algorithm. This is used in OpenPGP with elliptic curve keys.
|
||||
package keywrap
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrWrapPlaintext is returned if the plaintext is not a multiple
|
||||
// of 64 bits.
|
||||
ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits")
|
||||
|
||||
// ErrUnwrapCiphertext is returned if the ciphertext is not a
|
||||
// multiple of 64 bits.
|
||||
ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits")
|
||||
|
||||
// ErrUnwrapFailed is returned if unwrapping a key fails.
|
||||
ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key")
|
||||
|
||||
// NB: the AES NewCipher call only fails if the key is an invalid length.
|
||||
|
||||
// ErrInvalidKey is returned when the AES key is invalid.
|
||||
ErrInvalidKey = errors.New("keywrap: invalid AES key")
|
||||
)
|
||||
|
||||
// Wrap a key using the RFC 3394 AES Key Wrap Algorithm.
|
||||
func Wrap(key, plainText []byte) ([]byte, error) {
|
||||
if len(plainText)%8 != 0 {
|
||||
return nil, ErrWrapPlaintext
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
nblocks := len(plainText) / 8
|
||||
|
||||
// 1) Initialize variables.
|
||||
var block [aes.BlockSize]byte
|
||||
// - Set A = IV, an initial value (see 2.2.3)
|
||||
for ii := 0; ii < 8; ii++ {
|
||||
block[ii] = 0xA6
|
||||
}
|
||||
|
||||
// - For i = 1 to n
|
||||
// - Set R[i] = P[i]
|
||||
intermediate := make([]byte, len(plainText))
|
||||
copy(intermediate, plainText)
|
||||
|
||||
// 2) Calculate intermediate values.
|
||||
for ii := 0; ii < 6; ii++ {
|
||||
for jj := 0; jj < nblocks; jj++ {
|
||||
// - B = AES(K, A | R[i])
|
||||
copy(block[8:], intermediate[jj*8:jj*8+8])
|
||||
c.Encrypt(block[:], block[:])
|
||||
|
||||
// - A = MSB(64, B) ^ t where t = (n*j)+1
|
||||
t := uint64(ii*nblocks + jj + 1)
|
||||
val := binary.BigEndian.Uint64(block[:8]) ^ t
|
||||
binary.BigEndian.PutUint64(block[:8], val)
|
||||
|
||||
// - R[i] = LSB(64, B)
|
||||
copy(intermediate[jj*8:jj*8+8], block[8:])
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Output results.
|
||||
// - Set C[0] = A
|
||||
// - For i = 1 to n
|
||||
// - C[i] = R[i]
|
||||
return append(block[:8], intermediate...), nil
|
||||
}
|
||||
|
||||
// Unwrap a key using the RFC 3394 AES Key Wrap Algorithm.
|
||||
func Unwrap(key, cipherText []byte) ([]byte, error) {
|
||||
if len(cipherText)%8 != 0 {
|
||||
return nil, ErrUnwrapCiphertext
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
nblocks := len(cipherText)/8 - 1
|
||||
|
||||
// 1) Initialize variables.
|
||||
var block [aes.BlockSize]byte
|
||||
// - Set A = C[0]
|
||||
copy(block[:8], cipherText[:8])
|
||||
|
||||
// - For i = 1 to n
|
||||
// - Set R[i] = C[i]
|
||||
intermediate := make([]byte, len(cipherText)-8)
|
||||
copy(intermediate, cipherText[8:])
|
||||
|
||||
// 2) Compute intermediate values.
|
||||
for jj := 5; jj >= 0; jj-- {
|
||||
for ii := nblocks - 1; ii >= 0; ii-- {
|
||||
// - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1
|
||||
// - A = MSB(64, B)
|
||||
t := uint64(jj*nblocks + ii + 1)
|
||||
val := binary.BigEndian.Uint64(block[:8]) ^ t
|
||||
binary.BigEndian.PutUint64(block[:8], val)
|
||||
|
||||
copy(block[8:], intermediate[ii*8:ii*8+8])
|
||||
c.Decrypt(block[:], block[:])
|
||||
|
||||
// - R[i] = LSB(B, 64)
|
||||
copy(intermediate[ii*8:ii*8+8], block[8:])
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Output results.
|
||||
// - If A is an appropriate initial value (see 2.2.3),
|
||||
for ii := 0; ii < 8; ii++ {
|
||||
if block[ii] != 0xA6 {
|
||||
return nil, ErrUnwrapFailed
|
||||
}
|
||||
}
|
||||
|
||||
// - For i = 1 to n
|
||||
// - P[i] = R[i]
|
||||
return intermediate, nil
|
||||
}
|
224
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
generated
vendored
224
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
generated
vendored
|
@ -1,224 +0,0 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
||||
// very similar to PEM except that it has an additional CRC checksum.
|
||||
package armor // import "github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A Block represents an OpenPGP armored structure.
|
||||
//
|
||||
// The encoded form is:
|
||||
// -----BEGIN Type-----
|
||||
// Headers
|
||||
//
|
||||
// base64-encoded Bytes
|
||||
// '=' base64 encoded checksum
|
||||
// -----END Type-----
|
||||
// where Headers is a possibly empty sequence of Key: Value lines.
|
||||
//
|
||||
// Since the armored data can be very large, this package presents a streaming
|
||||
// interface.
|
||||
type Block struct {
|
||||
Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
|
||||
Header map[string]string // Optional headers.
|
||||
Body io.Reader // A Reader from which the contents can be read
|
||||
lReader lineReader
|
||||
oReader openpgpReader
|
||||
}
|
||||
|
||||
var ArmorCorrupt error = errors.StructuralError("armor invalid")
|
||||
|
||||
const crc24Init = 0xb704ce
|
||||
const crc24Poly = 0x1864cfb
|
||||
const crc24Mask = 0xffffff
|
||||
|
||||
// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
|
||||
func crc24(crc uint32, d []byte) uint32 {
|
||||
for _, b := range d {
|
||||
crc ^= uint32(b) << 16
|
||||
for i := 0; i < 8; i++ {
|
||||
crc <<= 1
|
||||
if crc&0x1000000 != 0 {
|
||||
crc ^= crc24Poly
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
var armorStart = []byte("-----BEGIN ")
|
||||
var armorEnd = []byte("-----END ")
|
||||
var armorEndOfLine = []byte("-----")
|
||||
|
||||
// lineReader wraps a line based reader. It watches for the end of an armor
|
||||
// block and records the expected CRC value.
|
||||
type lineReader struct {
|
||||
in *bufio.Reader
|
||||
buf []byte
|
||||
eof bool
|
||||
crc uint32
|
||||
crcSet bool
|
||||
}
|
||||
|
||||
func (l *lineReader) Read(p []byte) (n int, err error) {
|
||||
if l.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(l.buf) > 0 {
|
||||
n = copy(p, l.buf)
|
||||
l.buf = l.buf[n:]
|
||||
return
|
||||
}
|
||||
|
||||
line, isPrefix, err := l.in.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isPrefix {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(line, armorEnd) {
|
||||
l.eof = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(line) == 5 && line[0] == '=' {
|
||||
// This is the checksum line
|
||||
var expectedBytes [3]byte
|
||||
var m int
|
||||
m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
|
||||
if m != 3 || err != nil {
|
||||
return
|
||||
}
|
||||
l.crc = uint32(expectedBytes[0])<<16 |
|
||||
uint32(expectedBytes[1])<<8 |
|
||||
uint32(expectedBytes[2])
|
||||
|
||||
line, _, err = l.in.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return
|
||||
}
|
||||
if !bytes.HasPrefix(line, armorEnd) {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
l.eof = true
|
||||
l.crcSet = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(line) > 96 {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
n = copy(p, line)
|
||||
bytesToSave := len(line) - n
|
||||
if bytesToSave > 0 {
|
||||
if cap(l.buf) < bytesToSave {
|
||||
l.buf = make([]byte, 0, bytesToSave)
|
||||
}
|
||||
l.buf = l.buf[0:bytesToSave]
|
||||
copy(l.buf, line[n:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// openpgpReader passes Read calls to the underlying base64 decoder, but keeps
|
||||
// a running CRC of the resulting data and checks the CRC against the value
|
||||
// found by the lineReader at EOF.
|
||||
type openpgpReader struct {
|
||||
lReader *lineReader
|
||||
b64Reader io.Reader
|
||||
currentCRC uint32
|
||||
}
|
||||
|
||||
func (r *openpgpReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.b64Reader.Read(p)
|
||||
r.currentCRC = crc24(r.currentCRC, p[:n])
|
||||
|
||||
if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Decode reads a PGP armored block from the given Reader. It will ignore
|
||||
// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
|
||||
// given Reader is not usable after calling this function: an arbitrary amount
|
||||
// of data may have been read past the end of the block.
|
||||
func Decode(in io.Reader) (p *Block, err error) {
|
||||
r := bufio.NewReaderSize(in, 100)
|
||||
var line []byte
|
||||
ignoreNext := false
|
||||
|
||||
TryNextBlock:
|
||||
p = nil
|
||||
|
||||
// Skip leading garbage
|
||||
for {
|
||||
ignoreThis := ignoreNext
|
||||
line, ignoreNext, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ignoreNext || ignoreThis {
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
p = new(Block)
|
||||
p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
|
||||
p.Header = make(map[string]string)
|
||||
nextIsContinuation := false
|
||||
var lastKey string
|
||||
|
||||
// Read headers
|
||||
for {
|
||||
isContinuation := nextIsContinuation
|
||||
line, nextIsContinuation, err = r.ReadLine()
|
||||
if err != nil {
|
||||
p = nil
|
||||
return
|
||||
}
|
||||
if isContinuation {
|
||||
p.Header[lastKey] += string(line)
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i := bytes.Index(line, []byte(": "))
|
||||
if i == -1 {
|
||||
goto TryNextBlock
|
||||
}
|
||||
lastKey = string(line[:i])
|
||||
p.Header[lastKey] = string(line[i+2:])
|
||||
}
|
||||
|
||||
p.lReader.in = r
|
||||
p.oReader.currentCRC = crc24Init
|
||||
p.oReader.lReader = &p.lReader
|
||||
p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
|
||||
p.Body = &p.oReader
|
||||
|
||||
return
|
||||
}
|
160
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
generated
vendored
160
vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
generated
vendored
|
@ -1,160 +0,0 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package armor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
var armorHeaderSep = []byte(": ")
|
||||
var blockEnd = []byte("\n=")
|
||||
var newline = []byte("\n")
|
||||
var armorEndOfLineOut = []byte("-----\n")
|
||||
|
||||
// writeSlices writes its arguments to the given Writer.
|
||||
func writeSlices(out io.Writer, slices ...[]byte) (err error) {
|
||||
for _, s := range slices {
|
||||
_, err = out.Write(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lineBreaker breaks data across several lines, all of the same byte length
|
||||
// (except possibly the last). Lines are broken with a single '\n'.
|
||||
type lineBreaker struct {
|
||||
lineLength int
|
||||
line []byte
|
||||
used int
|
||||
out io.Writer
|
||||
haveWritten bool
|
||||
}
|
||||
|
||||
func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
|
||||
return &lineBreaker{
|
||||
lineLength: lineLength,
|
||||
line: make([]byte, lineLength),
|
||||
used: 0,
|
||||
out: out,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Write(b []byte) (n int, err error) {
|
||||
n = len(b)
|
||||
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if l.used == 0 && l.haveWritten {
|
||||
_, err = l.out.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.used+len(b) < l.lineLength {
|
||||
l.used += copy(l.line[l.used:], b)
|
||||
return
|
||||
}
|
||||
|
||||
l.haveWritten = true
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
excess := l.lineLength - l.used
|
||||
l.used = 0
|
||||
|
||||
_, err = l.out.Write(b[0:excess])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = l.Write(b[excess:])
|
||||
return
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Close() (err error) {
|
||||
if l.used > 0 {
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// encoding keeps track of a running CRC24 over the data which has been written
|
||||
// to it and outputs a OpenPGP checksum when closed, followed by an armor
|
||||
// trailer.
|
||||
//
|
||||
// It's built into a stack of io.Writers:
|
||||
// encoding -> base64 encoder -> lineBreaker -> out
|
||||
type encoding struct {
|
||||
out io.Writer
|
||||
breaker *lineBreaker
|
||||
b64 io.WriteCloser
|
||||
crc uint32
|
||||
blockType []byte
|
||||
}
|
||||
|
||||
func (e *encoding) Write(data []byte) (n int, err error) {
|
||||
e.crc = crc24(e.crc, data)
|
||||
return e.b64.Write(data)
|
||||
}
|
||||
|
||||
func (e *encoding) Close() (err error) {
|
||||
err = e.b64.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.breaker.Close()
|
||||
|
||||
var checksumBytes [3]byte
|
||||
checksumBytes[0] = byte(e.crc >> 16)
|
||||
checksumBytes[1] = byte(e.crc >> 8)
|
||||
checksumBytes[2] = byte(e.crc)
|
||||
|
||||
var b64ChecksumBytes [4]byte
|
||||
base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
|
||||
|
||||
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
|
||||
}
|
||||
|
||||
// Encode returns a WriteCloser which will encode the data written to it in
|
||||
// OpenPGP armor.
|
||||
func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
|
||||
bType := []byte(blockType)
|
||||
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e := &encoding{
|
||||
out: out,
|
||||
breaker: newLineBreaker(out, 64),
|
||||
crc: crc24Init,
|
||||
blockType: bType,
|
||||
}
|
||||
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
|
||||
return e, nil
|
||||
}
|
65
vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
generated
vendored
65
vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
generated
vendored
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package openpgp
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NewCanonicalTextHash reformats text written to it into the canonical
|
||||
// form and then applies the hash h. See RFC 4880, section 5.2.1.
|
||||
func NewCanonicalTextHash(h hash.Hash) hash.Hash {
|
||||
return &canonicalTextHash{h, 0}
|
||||
}
|
||||
|
||||
type canonicalTextHash struct {
|
||||
h hash.Hash
|
||||
s int
|
||||
}
|
||||
|
||||
var newline = []byte{'\r', '\n'}
|
||||
|
||||
func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) {
|
||||
start := 0
|
||||
for i, c := range buf {
|
||||
switch *s {
|
||||
case 0:
|
||||
if c == '\r' {
|
||||
*s = 1
|
||||
} else if c == '\n' {
|
||||
cw.Write(buf[start:i])
|
||||
cw.Write(newline)
|
||||
start = i + 1
|
||||
}
|
||||
case 1:
|
||||
*s = 0
|
||||
}
|
||||
}
|
||||
|
||||
cw.Write(buf[start:])
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
|
||||
return writeCanonical(cth.h, buf, &cth.s)
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Sum(in []byte) []byte {
|
||||
return cth.h.Sum(in)
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Reset() {
|
||||
cth.h.Reset()
|
||||
cth.s = 0
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Size() int {
|
||||
return cth.h.Size()
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) BlockSize() int {
|
||||
return cth.h.BlockSize()
|
||||
}
|
165
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
generated
vendored
165
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
generated
vendored
|
@ -1,165 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
|
||||
// as specified in RFC 6637, section 8.
|
||||
package ecdh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
)
|
||||
|
||||
type KDF struct {
|
||||
Hash algorithm.Hash
|
||||
Cipher algorithm.Cipher
|
||||
}
|
||||
|
||||
type PublicKey struct {
|
||||
ecc.CurveType
|
||||
elliptic.Curve
|
||||
X, Y *big.Int
|
||||
KDF
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
D []byte
|
||||
}
|
||||
|
||||
func GenerateKey(c elliptic.Curve, kdf KDF, rand io.Reader) (priv *PrivateKey, err error) {
|
||||
priv = new(PrivateKey)
|
||||
priv.PublicKey.Curve = c
|
||||
priv.PublicKey.KDF = kdf
|
||||
priv.D, priv.PublicKey.X, priv.PublicKey.Y, err = elliptic.GenerateKey(c, rand)
|
||||
return
|
||||
}
|
||||
|
||||
func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
|
||||
if len(msg) > 40 {
|
||||
return nil, nil, errors.New("ecdh: message too long")
|
||||
}
|
||||
// the sender MAY use 21, 13, and 5 bytes of padding for AES-128,
|
||||
// AES-192, and AES-256, respectively, to provide the same number of
|
||||
// octets, 40 total, as an input to the key wrapping method.
|
||||
padding := make([]byte, 40-len(msg))
|
||||
for i := range padding {
|
||||
padding[i] = byte(40 - len(msg))
|
||||
}
|
||||
m := append(msg, padding...)
|
||||
|
||||
if pub.CurveType == ecc.Curve25519 {
|
||||
return X25519Encrypt(random, pub, m, curveOID, fingerprint)
|
||||
}
|
||||
|
||||
d, x, y, err := elliptic.GenerateKey(pub.Curve, random)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
vsG = elliptic.Marshal(pub.Curve, x, y)
|
||||
zbBig, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d)
|
||||
|
||||
byteLen := (pub.Curve.Params().BitSize + 7) >> 3
|
||||
zb := make([]byte, byteLen)
|
||||
zbBytes := zbBig.Bytes()
|
||||
copy(zb[byteLen-len(zbBytes):], zbBytes)
|
||||
|
||||
z, err := buildKey(pub, zb, curveOID, fingerprint, false, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if c, err = keywrap.Wrap(z, m); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return vsG, c, nil
|
||||
|
||||
}
|
||||
|
||||
func Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
|
||||
if priv.PublicKey.CurveType == ecc.Curve25519 {
|
||||
return X25519Decrypt(priv, vsG, m, curveOID, fingerprint)
|
||||
}
|
||||
x, y := elliptic.Unmarshal(priv.Curve, vsG)
|
||||
zbBig, _ := priv.Curve.ScalarMult(x, y, priv.D)
|
||||
|
||||
byteLen := (priv.Curve.Params().BitSize + 7) >> 3
|
||||
zb := make([]byte, byteLen)
|
||||
zbBytes := zbBig.Bytes()
|
||||
copy(zb[byteLen-len(zbBytes):], zbBytes)
|
||||
|
||||
z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := keywrap.Unwrap(z, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c[:len(c)-int(c[len(c)-1])], nil
|
||||
}
|
||||
|
||||
func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLeading, stripTrailing bool) ([]byte, error) {
|
||||
// Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
|
||||
// || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap
|
||||
// || "Anonymous Sender " || recipient_fingerprint;
|
||||
param := new(bytes.Buffer)
|
||||
if _, err := param.Write(curveOID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
algKDF := []byte{18, 3, 1, pub.KDF.Hash.Id(), pub.KDF.Cipher.Id()}
|
||||
if _, err := param.Write(algKDF); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := param.Write([]byte("Anonymous Sender ")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// For v5 keys, the 20 leftmost octets of the fingerprint are used.
|
||||
if _, err := param.Write(fingerprint[:20]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if param.Len() - len(curveOID) != 45 {
|
||||
return nil, errors.New("ecdh: malformed KDF Param")
|
||||
}
|
||||
|
||||
// MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
|
||||
h := pub.KDF.Hash.New()
|
||||
if _, err := h.Write([]byte{0x0, 0x0, 0x0, 0x1}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zbLen := len(zb)
|
||||
i := 0
|
||||
j := zbLen - 1
|
||||
if stripLeading {
|
||||
// Work around old go crypto bug where the leading zeros are missing.
|
||||
for ; i < zbLen && zb[i] == 0; i++ {}
|
||||
}
|
||||
if stripTrailing {
|
||||
// Work around old OpenPGP.js bug where insignificant trailing zeros in
|
||||
// this little-endian number are missing.
|
||||
// (See https://github.com/openpgpjs/openpgpjs/pull/853.)
|
||||
for ; j >= 0 && zb[j] == 0; j-- {}
|
||||
}
|
||||
if _, err := h.Write(zb[i:j+1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := h.Write(param.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mb := h.Sum(nil)
|
||||
|
||||
return mb[:pub.KDF.Cipher.KeySize()], nil // return oBits leftmost bits of MB.
|
||||
|
||||
}
|
157
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/x25519.go
generated
vendored
157
vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/x25519.go
generated
vendored
|
@ -1,157 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
|
||||
// as specified in RFC 6637, section 8.
|
||||
package ecdh
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
// Generates a private-public key-pair.
|
||||
// 'priv' is a private key; a scalar belonging to the set
|
||||
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the
|
||||
// curve. 'pub' is simply 'priv' * G where G is the base point.
|
||||
// See https://cr.yp.to/ecdh.html and RFC7748, sec 5.
|
||||
func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) {
|
||||
var n, helper = new(big.Int), new(big.Int)
|
||||
n.SetUint64(1)
|
||||
n.Lsh(n, 252)
|
||||
helper.SetString("27742317777372353535851937790883648493", 10)
|
||||
n.Add(n, helper)
|
||||
|
||||
for true {
|
||||
_, err = io.ReadFull(rand, priv[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// The following ensures that the private key is a number of the form
|
||||
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of
|
||||
// of the curve.
|
||||
priv[0] &= 248
|
||||
priv[31] &= 127
|
||||
priv[31] |= 64
|
||||
|
||||
// If the scalar is out of range, sample another random number.
|
||||
if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
curve25519.ScalarBaseMult(&pub, &priv)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// X25519GenerateKey samples the key pair according to the correct distribution.
|
||||
// It also sets the given key-derivation function and returns the *PrivateKey
|
||||
// object along with an error.
|
||||
func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) {
|
||||
ci := ecc.FindByName("Curve25519")
|
||||
priv = new(PrivateKey)
|
||||
priv.PublicKey.Curve = ci.Curve
|
||||
d, pubKey, err := x25519GenerateKeyPairBytes(rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv.PublicKey.KDF = kdf
|
||||
priv.D = make([]byte, 32)
|
||||
copyReversed(priv.D, d[:])
|
||||
priv.PublicKey.CurveType = ci.CurveType
|
||||
priv.PublicKey.Curve = ci.Curve
|
||||
/*
|
||||
* Note that ECPoint.point differs from the definition of public keys in
|
||||
* [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is
|
||||
* more uniform with how big integers are represented in TLS, and (2) there
|
||||
* is an additional length byte (so ECpoint.point is actually 33 bytes),
|
||||
* again for uniformity (and extensibility).
|
||||
*/
|
||||
var encodedKey = make([]byte, 33)
|
||||
encodedKey[0] = 0x40
|
||||
copy(encodedKey[1:], pubKey[:])
|
||||
priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:])
|
||||
priv.PublicKey.Y = new(big.Int)
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
|
||||
d, ephemeralKey, err := x25519GenerateKeyPairBytes(random)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var pubKey [32]byte
|
||||
|
||||
if pub.X.BitLen() > 33*264 {
|
||||
return nil, nil, errors.New("ecdh: invalid key")
|
||||
}
|
||||
copy(pubKey[:], pub.X.Bytes()[1:])
|
||||
|
||||
var zb [32]byte
|
||||
curve25519.ScalarBaseMult(&zb, &d)
|
||||
curve25519.ScalarMult(&zb, &d, &pubKey)
|
||||
z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if c, err = keywrap.Wrap(z, msg); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var vsg [33]byte
|
||||
vsg[0] = 0x40
|
||||
copy(vsg[1:], ephemeralKey[:])
|
||||
|
||||
return vsg[:], c, nil
|
||||
}
|
||||
|
||||
func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
|
||||
var zb, d, ephemeralKey [32]byte
|
||||
if len(vsG) != 33 || vsG[0] != 0x40 {
|
||||
return nil, errors.New("ecdh: invalid key")
|
||||
}
|
||||
copy(ephemeralKey[:], vsG[1:33])
|
||||
|
||||
copyReversed(d[:], priv.D)
|
||||
curve25519.ScalarBaseMult(&zb, &d)
|
||||
curve25519.ScalarMult(&zb, &d, &ephemeralKey)
|
||||
|
||||
var c []byte
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
// Try buildKey three times for compat, see comments in buildKey.
|
||||
z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := keywrap.Unwrap(z, m)
|
||||
if i == 2 && err != nil {
|
||||
// Only return an error after we've tried all variants of buildKey.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = res
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return c[:len(c)-int(c[len(c)-1])], nil
|
||||
}
|
||||
|
||||
func copyReversed(out []byte, in []byte) {
|
||||
l := len(in)
|
||||
for i := 0; i < l; i++ {
|
||||
out[i] = in[l-i-1]
|
||||
}
|
||||
}
|
124
vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go
generated
vendored
124
vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go
generated
vendored
|
@ -1,124 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package elgamal implements ElGamal encryption, suitable for OpenPGP,
|
||||
// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on
|
||||
// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31,
|
||||
// n. 4, 1985, pp. 469-472.
|
||||
//
|
||||
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
|
||||
// unsuitable for other protocols. RSA should be used in preference in any
|
||||
// case.
|
||||
package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// PublicKey represents an ElGamal public key.
|
||||
type PublicKey struct {
|
||||
G, P, Y *big.Int
|
||||
}
|
||||
|
||||
// PrivateKey represents an ElGamal private key.
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
X *big.Int
|
||||
}
|
||||
|
||||
// Encrypt encrypts the given message to the given public key. The result is a
|
||||
// pair of integers. Errors can result from reading random, or because msg is
|
||||
// too large to be encrypted to the public key.
|
||||
func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) {
|
||||
pLen := (pub.P.BitLen() + 7) / 8
|
||||
if len(msg) > pLen-11 {
|
||||
err = errors.New("elgamal: message too long")
|
||||
return
|
||||
}
|
||||
|
||||
// EM = 0x02 || PS || 0x00 || M
|
||||
em := make([]byte, pLen-1)
|
||||
em[0] = 2
|
||||
ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
|
||||
err = nonZeroRandomBytes(ps, random)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
em[len(em)-len(msg)-1] = 0
|
||||
copy(mm, msg)
|
||||
|
||||
m := new(big.Int).SetBytes(em)
|
||||
|
||||
k, err := rand.Int(random, pub.P)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c1 = new(big.Int).Exp(pub.G, k, pub.P)
|
||||
s := new(big.Int).Exp(pub.Y, k, pub.P)
|
||||
c2 = s.Mul(s, m)
|
||||
c2.Mod(c2, pub.P)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt takes two integers, resulting from an ElGamal encryption, and
|
||||
// returns the plaintext of the message. An error can result only if the
|
||||
// ciphertext is invalid. Users should keep in mind that this is a padding
|
||||
// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
|
||||
// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks
|
||||
// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel
|
||||
// Bleichenbacher, Advances in Cryptology (Crypto '98),
|
||||
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
|
||||
s := new(big.Int).Exp(c1, priv.X, priv.P)
|
||||
if s.ModInverse(s, priv.P) == nil {
|
||||
return nil, errors.New("elgamal: invalid private key")
|
||||
}
|
||||
s.Mul(s, c2)
|
||||
s.Mod(s, priv.P)
|
||||
em := s.Bytes()
|
||||
|
||||
firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
|
||||
|
||||
// The remainder of the plaintext must be a string of non-zero random
|
||||
// octets, followed by a 0, followed by the message.
|
||||
// lookingForIndex: 1 iff we are still looking for the zero.
|
||||
// index: the offset of the first zero byte.
|
||||
var lookingForIndex, index int
|
||||
lookingForIndex = 1
|
||||
|
||||
for i := 1; i < len(em); i++ {
|
||||
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
|
||||
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
|
||||
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
|
||||
}
|
||||
|
||||
if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
|
||||
return nil, errors.New("elgamal: decryption error")
|
||||
}
|
||||
return em[index+1:], nil
|
||||
}
|
||||
|
||||
// nonZeroRandomBytes fills the given slice with non-zero random octets.
|
||||
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
|
||||
_, err = io.ReadFull(rand, s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
for s[i] == 0 {
|
||||
_, err = io.ReadFull(rand, s[i:i+1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
116
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
116
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
|
@ -1,116 +0,0 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package errors contains common error types for the OpenPGP packages.
|
||||
package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A StructuralError is returned when OpenPGP data is found to be syntactically
|
||||
// invalid.
|
||||
type StructuralError string
|
||||
|
||||
func (s StructuralError) Error() string {
|
||||
return "openpgp: invalid data: " + string(s)
|
||||
}
|
||||
|
||||
// UnsupportedError indicates that, although the OpenPGP data is valid, it
|
||||
// makes use of currently unimplemented features.
|
||||
type UnsupportedError string
|
||||
|
||||
func (s UnsupportedError) Error() string {
|
||||
return "openpgp: unsupported feature: " + string(s)
|
||||
}
|
||||
|
||||
// InvalidArgumentError indicates that the caller is in error and passed an
|
||||
// incorrect value.
|
||||
type InvalidArgumentError string
|
||||
|
||||
func (i InvalidArgumentError) Error() string {
|
||||
return "openpgp: invalid argument: " + string(i)
|
||||
}
|
||||
|
||||
// SignatureError indicates that a syntactically valid signature failed to
|
||||
// validate.
|
||||
type SignatureError string
|
||||
|
||||
func (b SignatureError) Error() string {
|
||||
return "openpgp: invalid signature: " + string(b)
|
||||
}
|
||||
|
||||
var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch")
|
||||
var ErrMDCMissing error = SignatureError("MDC packet not found")
|
||||
|
||||
type signatureExpiredError int
|
||||
|
||||
func (se signatureExpiredError) Error() string {
|
||||
return "openpgp: signature expired"
|
||||
}
|
||||
|
||||
var ErrSignatureExpired error = signatureExpiredError(0)
|
||||
|
||||
type keyExpiredError int
|
||||
|
||||
func (ke keyExpiredError) Error() string {
|
||||
return "openpgp: key expired"
|
||||
}
|
||||
|
||||
var ErrKeyExpired error = keyExpiredError(0)
|
||||
|
||||
type keyIncorrectError int
|
||||
|
||||
func (ki keyIncorrectError) Error() string {
|
||||
return "openpgp: incorrect key"
|
||||
}
|
||||
|
||||
var ErrKeyIncorrect error = keyIncorrectError(0)
|
||||
|
||||
// KeyInvalidError indicates that the public key parameters are invalid
|
||||
// as they do not match the private ones
|
||||
type KeyInvalidError string
|
||||
|
||||
func (e KeyInvalidError) Error() string {
|
||||
return "openpgp: invalid key: " + string(e)
|
||||
}
|
||||
|
||||
type unknownIssuerError int
|
||||
|
||||
func (unknownIssuerError) Error() string {
|
||||
return "openpgp: signature made by unknown entity"
|
||||
}
|
||||
|
||||
var ErrUnknownIssuer error = unknownIssuerError(0)
|
||||
|
||||
type keyRevokedError int
|
||||
|
||||
func (keyRevokedError) Error() string {
|
||||
return "openpgp: signature made by revoked key"
|
||||
}
|
||||
|
||||
var ErrKeyRevoked error = keyRevokedError(0)
|
||||
|
||||
type UnknownPacketTypeError uint8
|
||||
|
||||
func (upte UnknownPacketTypeError) Error() string {
|
||||
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
|
||||
}
|
||||
|
||||
// AEADError indicates that there is a problem when initializing or using a
|
||||
// AEAD instance, configuration struct, nonces or index values.
|
||||
type AEADError string
|
||||
|
||||
func (ae AEADError) Error() string {
|
||||
return "openpgp: aead error: " + string(ae)
|
||||
}
|
||||
|
||||
// ErrDummyPrivateKey results when operations are attempted on a private key
|
||||
// that is just a dummy key. See
|
||||
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
|
||||
type ErrDummyPrivateKey string
|
||||
|
||||
func (dke ErrDummyPrivateKey) Error() string {
|
||||
return "openpgp: s2k GNU dummy key: " + string(dke)
|
||||
}
|
65
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go
generated
vendored
65
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.go
generated
vendored
|
@ -1,65 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"github.com/ProtonMail/go-crypto/eax"
|
||||
"github.com/ProtonMail/go-crypto/ocb"
|
||||
)
|
||||
|
||||
// AEADMode defines the Authenticated Encryption with Associated Data mode of
|
||||
// operation.
|
||||
type AEADMode uint8
|
||||
|
||||
// Supported modes of operation (see RFC4880bis [EAX] and RFC7253)
|
||||
const (
|
||||
AEADModeEAX = AEADMode(1)
|
||||
AEADModeOCB = AEADMode(2)
|
||||
AEADModeGCM = AEADMode(100)
|
||||
)
|
||||
|
||||
// TagLength returns the length in bytes of authentication tags.
|
||||
func (mode AEADMode) TagLength() int {
|
||||
switch mode {
|
||||
case AEADModeEAX:
|
||||
return 16
|
||||
case AEADModeOCB:
|
||||
return 16
|
||||
case AEADModeGCM:
|
||||
return 16
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// NonceLength returns the length in bytes of nonces.
|
||||
func (mode AEADMode) NonceLength() int {
|
||||
switch mode {
|
||||
case AEADModeEAX:
|
||||
return 16
|
||||
case AEADModeOCB:
|
||||
return 15
|
||||
case AEADModeGCM:
|
||||
return 12
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a fresh instance of the given mode
|
||||
func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) {
|
||||
var err error
|
||||
switch mode {
|
||||
case AEADModeEAX:
|
||||
alg, err = eax.NewEAX(block)
|
||||
case AEADModeOCB:
|
||||
alg, err = ocb.NewOCB(block)
|
||||
case AEADModeGCM:
|
||||
alg, err = cipher.NewGCM(block)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return alg
|
||||
}
|
107
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
generated
vendored
107
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
generated
vendored
|
@ -1,107 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
|
||||
"golang.org/x/crypto/cast5"
|
||||
)
|
||||
|
||||
// Cipher is an official symmetric key cipher algorithm. See RFC 4880,
|
||||
// section 9.2.
|
||||
type Cipher interface {
|
||||
// Id returns the algorithm ID, as a byte, of the cipher.
|
||||
Id() uint8
|
||||
// KeySize returns the key size, in bytes, of the cipher.
|
||||
KeySize() int
|
||||
// BlockSize returns the block size, in bytes, of the cipher.
|
||||
BlockSize() int
|
||||
// New returns a fresh instance of the given cipher.
|
||||
New(key []byte) cipher.Block
|
||||
}
|
||||
|
||||
// The following constants mirror the OpenPGP standard (RFC 4880).
|
||||
const (
|
||||
TripleDES = CipherFunction(2)
|
||||
CAST5 = CipherFunction(3)
|
||||
AES128 = CipherFunction(7)
|
||||
AES192 = CipherFunction(8)
|
||||
AES256 = CipherFunction(9)
|
||||
)
|
||||
|
||||
// CipherById represents the different block ciphers specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
|
||||
var CipherById = map[uint8]Cipher{
|
||||
TripleDES.Id(): TripleDES,
|
||||
CAST5.Id(): CAST5,
|
||||
AES128.Id(): AES128,
|
||||
AES192.Id(): AES192,
|
||||
AES256.Id(): AES256,
|
||||
}
|
||||
|
||||
type CipherFunction uint8
|
||||
|
||||
// ID returns the algorithm Id, as a byte, of cipher.
|
||||
func (sk CipherFunction) Id() uint8 {
|
||||
return uint8(sk)
|
||||
}
|
||||
|
||||
var keySizeByID = map[uint8]int{
|
||||
TripleDES.Id(): 24,
|
||||
CAST5.Id(): cast5.KeySize,
|
||||
AES128.Id(): 16,
|
||||
AES192.Id(): 24,
|
||||
AES256.Id(): 32,
|
||||
}
|
||||
|
||||
// KeySize returns the key size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) KeySize() int {
|
||||
switch cipher {
|
||||
case TripleDES:
|
||||
return 24
|
||||
case CAST5:
|
||||
return cast5.KeySize
|
||||
case AES128:
|
||||
return 16
|
||||
case AES192:
|
||||
return 24
|
||||
case AES256:
|
||||
return 32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// BlockSize returns the block size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) BlockSize() int {
|
||||
switch cipher {
|
||||
case TripleDES:
|
||||
return des.BlockSize
|
||||
case CAST5:
|
||||
return 8
|
||||
case AES128, AES192, AES256:
|
||||
return 16
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// New returns a fresh instance of the given cipher.
|
||||
func (cipher CipherFunction) New(key []byte) (block cipher.Block) {
|
||||
var err error
|
||||
switch cipher {
|
||||
case TripleDES:
|
||||
block, err = des.NewTripleDESCipher(key)
|
||||
case CAST5:
|
||||
block, err = cast5.NewCipher(key)
|
||||
case AES128, AES192, AES256:
|
||||
block, err = aes.NewCipher(key)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
86
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go
generated
vendored
86
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.go
generated
vendored
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package algorithm
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Hash is an official hash function algorithm. See RFC 4880, section 9.4.
|
||||
type Hash interface {
|
||||
// Id returns the algorithm ID, as a byte, of Hash.
|
||||
Id() uint8
|
||||
// Available reports whether the given hash function is linked into the binary.
|
||||
Available() bool
|
||||
// HashFunc simply returns the value of h so that Hash implements SignerOpts.
|
||||
HashFunc() crypto.Hash
|
||||
// New returns a new hash.Hash calculating the given hash function. New
|
||||
// panics if the hash function is not linked into the binary.
|
||||
New() hash.Hash
|
||||
// Size returns the length, in bytes, of a digest resulting from the given
|
||||
// hash function. It doesn't require that the hash function in question be
|
||||
// linked into the program.
|
||||
Size() int
|
||||
// String is the name of the hash function corresponding to the given
|
||||
// OpenPGP hash id.
|
||||
String() string
|
||||
}
|
||||
|
||||
// The following vars mirror the crypto/Hash supported hash functions.
|
||||
var (
|
||||
MD5 Hash = cryptoHash{1, crypto.MD5}
|
||||
SHA1 Hash = cryptoHash{2, crypto.SHA1}
|
||||
RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160}
|
||||
SHA256 Hash = cryptoHash{8, crypto.SHA256}
|
||||
SHA384 Hash = cryptoHash{9, crypto.SHA384}
|
||||
SHA512 Hash = cryptoHash{10, crypto.SHA512}
|
||||
SHA224 Hash = cryptoHash{11, crypto.SHA224}
|
||||
)
|
||||
|
||||
// HashById represents the different hash functions specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14
|
||||
var (
|
||||
HashById = map[uint8]Hash{
|
||||
MD5.Id(): MD5,
|
||||
SHA1.Id(): SHA1,
|
||||
RIPEMD160.Id(): RIPEMD160,
|
||||
SHA256.Id(): SHA256,
|
||||
SHA384.Id(): SHA384,
|
||||
SHA512.Id(): SHA512,
|
||||
SHA224.Id(): SHA224,
|
||||
}
|
||||
)
|
||||
|
||||
// cryptoHash contains pairs relating OpenPGP's hash identifier with
|
||||
// Go's crypto.Hash type. See RFC 4880, section 9.4.
|
||||
type cryptoHash struct {
|
||||
id uint8
|
||||
crypto.Hash
|
||||
}
|
||||
|
||||
// Id returns the algorithm ID, as a byte, of cryptoHash.
|
||||
func (h cryptoHash) Id() uint8 {
|
||||
return h.id
|
||||
}
|
||||
|
||||
var hashNames = map[uint8]string{
|
||||
MD5.Id(): "MD5",
|
||||
SHA1.Id(): "SHA1",
|
||||
RIPEMD160.Id(): "RIPEMD160",
|
||||
SHA256.Id(): "SHA256",
|
||||
SHA384.Id(): "SHA384",
|
||||
SHA512.Id(): "SHA512",
|
||||
SHA224.Id(): "SHA224",
|
||||
}
|
||||
|
||||
func (h cryptoHash) String() string {
|
||||
s, ok := hashNames[h.id]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unsupported hash function %d", h.id))
|
||||
}
|
||||
return s
|
||||
}
|
118
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveInfo.go
generated
vendored
118
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveInfo.go
generated
vendored
|
@ -1,118 +0,0 @@
|
|||
package ecc
|
||||
|
||||
import (
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"crypto/elliptic"
|
||||
"bytes"
|
||||
"github.com/ProtonMail/go-crypto/bitcurves"
|
||||
"github.com/ProtonMail/go-crypto/brainpool"
|
||||
)
|
||||
|
||||
type SignatureAlgorithm uint8
|
||||
|
||||
const (
|
||||
ECDSA SignatureAlgorithm = 1
|
||||
EdDSA SignatureAlgorithm = 2
|
||||
)
|
||||
|
||||
type CurveInfo struct {
|
||||
Name string
|
||||
Oid *encoding.OID
|
||||
Curve elliptic.Curve
|
||||
SigAlgorithm SignatureAlgorithm
|
||||
CurveType CurveType
|
||||
}
|
||||
|
||||
var curves = []CurveInfo{
|
||||
{
|
||||
Name: "NIST curve P-256",
|
||||
Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}),
|
||||
Curve: elliptic.P256(),
|
||||
CurveType: NISTCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "NIST curve P-384",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}),
|
||||
Curve: elliptic.P384(),
|
||||
CurveType: NISTCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "NIST curve P-521",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}),
|
||||
Curve: elliptic.P521(),
|
||||
CurveType: NISTCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "SecP256k1",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}),
|
||||
Curve: bitcurves.S256(),
|
||||
CurveType: BitCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "Curve25519",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
|
||||
Curve: elliptic.P256(),// filler
|
||||
CurveType: Curve25519,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "Ed25519",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
|
||||
Curve: elliptic.P256(), // filler
|
||||
CurveType: NISTCurve,
|
||||
SigAlgorithm: EdDSA,
|
||||
},
|
||||
{
|
||||
Name: "Brainpool P256r1",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}),
|
||||
Curve: brainpool.P256r1(),
|
||||
CurveType: BrainpoolCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "BrainpoolP384r1",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}),
|
||||
Curve: brainpool.P384r1(),
|
||||
CurveType: BrainpoolCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
{
|
||||
Name: "BrainpoolP512r1",
|
||||
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}),
|
||||
Curve: brainpool.P512r1(),
|
||||
CurveType: BrainpoolCurve,
|
||||
SigAlgorithm: ECDSA,
|
||||
},
|
||||
}
|
||||
|
||||
func FindByCurve(curve elliptic.Curve) *CurveInfo {
|
||||
for _, curveInfo := range curves {
|
||||
if curveInfo.Curve == curve {
|
||||
return &curveInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindByOid(oid encoding.Field) *CurveInfo {
|
||||
var rawBytes = oid.Bytes()
|
||||
for _, curveInfo := range curves {
|
||||
if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) {
|
||||
return &curveInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindByName(name string) *CurveInfo {
|
||||
for _, curveInfo := range curves {
|
||||
if curveInfo.Name == name {
|
||||
return &curveInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
10
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveType.go
generated
vendored
10
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curveType.go
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
package ecc
|
||||
|
||||
type CurveType uint8
|
||||
|
||||
const (
|
||||
NISTCurve CurveType = 1
|
||||
Curve25519 CurveType = 2
|
||||
BitCurve CurveType = 3
|
||||
BrainpoolCurve CurveType = 4
|
||||
)
|
27
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go
generated
vendored
27
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.go
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package encoding implements openpgp packet field encodings as specified in
|
||||
// RFC 4880 and 6637.
|
||||
package encoding
|
||||
|
||||
import "io"
|
||||
|
||||
// Field is an encoded field of an openpgp packet.
|
||||
type Field interface {
|
||||
// Bytes returns the decoded data.
|
||||
Bytes() []byte
|
||||
|
||||
// BitLength is the size in bits of the decoded data.
|
||||
BitLength() uint16
|
||||
|
||||
// EncodedBytes returns the encoded data.
|
||||
EncodedBytes() []byte
|
||||
|
||||
// EncodedLength is the size in bytes of the encoded data.
|
||||
EncodedLength() uint16
|
||||
|
||||
// ReadFrom reads the next Field from r.
|
||||
ReadFrom(r io.Reader) (int64, error)
|
||||
}
|
91
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go
generated
vendored
91
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.go
generated
vendored
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// An MPI is used to store the contents of a big integer, along with the bit
|
||||
// length that was specified in the original input. This allows the MPI to be
|
||||
// reserialized exactly.
|
||||
type MPI struct {
|
||||
bytes []byte
|
||||
bitLength uint16
|
||||
}
|
||||
|
||||
// NewMPI returns a MPI initialized with bytes.
|
||||
func NewMPI(bytes []byte) *MPI {
|
||||
for len(bytes) != 0 && bytes[0] == 0 {
|
||||
bytes = bytes[1:]
|
||||
}
|
||||
if len(bytes) == 0 {
|
||||
bitLength := uint16(0)
|
||||
return &MPI{bytes, bitLength}
|
||||
}
|
||||
bitLength := 8*uint16(len(bytes)-1) + uint16(bits.Len8(bytes[0]))
|
||||
return &MPI{bytes, bitLength}
|
||||
}
|
||||
|
||||
// Bytes returns the decoded data.
|
||||
func (m *MPI) Bytes() []byte {
|
||||
return m.bytes
|
||||
}
|
||||
|
||||
// BitLength is the size in bits of the decoded data.
|
||||
func (m *MPI) BitLength() uint16 {
|
||||
return m.bitLength
|
||||
}
|
||||
|
||||
// EncodedBytes returns the encoded data.
|
||||
func (m *MPI) EncodedBytes() []byte {
|
||||
return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...)
|
||||
}
|
||||
|
||||
// EncodedLength is the size in bytes of the encoded data.
|
||||
func (m *MPI) EncodedLength() uint16 {
|
||||
return uint16(2 + len(m.bytes))
|
||||
}
|
||||
|
||||
// ReadFrom reads into m the next MPI from r.
|
||||
func (m *MPI) ReadFrom(r io.Reader) (int64, error) {
|
||||
var buf [2]byte
|
||||
n, err := io.ReadFull(r, buf[0:])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
m.bitLength = uint16(buf[0])<<8 | uint16(buf[1])
|
||||
m.bytes = make([]byte, (int(m.bitLength)+7)/8)
|
||||
|
||||
nn, err := io.ReadFull(r, m.bytes)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// remove leading zero bytes from malformed GnuPG encoded MPIs:
|
||||
// https://bugs.gnupg.org/gnupg/issue1853
|
||||
// for _, b := range m.bytes {
|
||||
// if b != 0 {
|
||||
// break
|
||||
// }
|
||||
// m.bytes = m.bytes[1:]
|
||||
// m.bitLength -= 8
|
||||
// }
|
||||
|
||||
return int64(n) + int64(nn), err
|
||||
}
|
||||
|
||||
// SetBig initializes m with the bits from n.
|
||||
func (m *MPI) SetBig(n *big.Int) *MPI {
|
||||
m.bytes = n.Bytes()
|
||||
m.bitLength = uint16(n.BitLen())
|
||||
return m
|
||||
}
|
88
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go
generated
vendored
88
vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.go
generated
vendored
|
@ -1,88 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// OID is used to store a variable-length field with a one-octet size
|
||||
// prefix. See https://tools.ietf.org/html/rfc6637#section-9.
|
||||
type OID struct {
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
const (
|
||||
// maxOID is the maximum number of bytes in a OID.
|
||||
maxOID = 254
|
||||
// reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC
|
||||
// specifies are reserved.
|
||||
reservedOIDLength1 = 0
|
||||
reservedOIDLength2 = 0xff
|
||||
)
|
||||
|
||||
// NewOID returns a OID initialized with bytes.
|
||||
func NewOID(bytes []byte) *OID {
|
||||
switch len(bytes) {
|
||||
case reservedOIDLength1, reservedOIDLength2:
|
||||
panic("encoding: NewOID argument length is reserved")
|
||||
default:
|
||||
if len(bytes) > maxOID {
|
||||
panic("encoding: NewOID argment too large")
|
||||
}
|
||||
}
|
||||
|
||||
return &OID{
|
||||
bytes: bytes,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns the decoded data.
|
||||
func (o *OID) Bytes() []byte {
|
||||
return o.bytes
|
||||
}
|
||||
|
||||
// BitLength is the size in bits of the decoded data.
|
||||
func (o *OID) BitLength() uint16 {
|
||||
return uint16(len(o.bytes) * 8)
|
||||
}
|
||||
|
||||
// EncodedBytes returns the encoded data.
|
||||
func (o *OID) EncodedBytes() []byte {
|
||||
return append([]byte{byte(len(o.bytes))}, o.bytes...)
|
||||
}
|
||||
|
||||
// EncodedLength is the size in bytes of the encoded data.
|
||||
func (o *OID) EncodedLength() uint16 {
|
||||
return uint16(1 + len(o.bytes))
|
||||
}
|
||||
|
||||
// ReadFrom reads into b the next OID from r.
|
||||
func (o *OID) ReadFrom(r io.Reader) (int64, error) {
|
||||
var buf [1]byte
|
||||
n, err := io.ReadFull(r, buf[:])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case reservedOIDLength1, reservedOIDLength2:
|
||||
return int64(n), errors.UnsupportedError("reserved for future extensions")
|
||||
}
|
||||
|
||||
o.bytes = make([]byte, buf[0])
|
||||
|
||||
nn, err := io.ReadFull(r, o.bytes)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return int64(n) + int64(nn), err
|
||||
}
|
375
vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
generated
vendored
375
vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
generated
vendored
|
@ -1,375 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package openpgp
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
goerrors "errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
|
||||
// single identity composed of the given full name, comment and email, any of
|
||||
// which may be empty but must not contain any of "()<>\x00".
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
|
||||
creationTime := config.Now()
|
||||
keyLifetimeSecs := config.KeyLifetime()
|
||||
|
||||
uid := packet.NewUserId(name, comment, email)
|
||||
if uid == nil {
|
||||
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
|
||||
}
|
||||
|
||||
// Generate a primary signing key
|
||||
primaryPrivRaw, err := newSigner(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
|
||||
if config != nil && config.V5Keys {
|
||||
primary.UpgradeToV5()
|
||||
}
|
||||
|
||||
isPrimaryId := true
|
||||
selfSignature := &packet.Signature{
|
||||
Version: primary.PublicKey.Version,
|
||||
SigType: packet.SigTypePositiveCert,
|
||||
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
CreationTime: creationTime,
|
||||
KeyLifetimeSecs: &keyLifetimeSecs,
|
||||
IssuerKeyId: &primary.PublicKey.KeyId,
|
||||
IssuerFingerprint: primary.PublicKey.Fingerprint,
|
||||
IsPrimaryId: &isPrimaryId,
|
||||
FlagsValid: true,
|
||||
FlagSign: true,
|
||||
FlagCertify: true,
|
||||
MDC: true, // true by default, see 5.8 vs. 5.14
|
||||
AEAD: config.AEAD() != nil,
|
||||
V5Keys: config != nil && config.V5Keys,
|
||||
}
|
||||
|
||||
// Set the PreferredHash for the SelfSignature from the packet.Config.
|
||||
// If it is not the must-implement algorithm from rfc4880bis, append that.
|
||||
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())}
|
||||
if config.Hash() != crypto.SHA256 {
|
||||
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256))
|
||||
}
|
||||
|
||||
// Likewise for DefaultCipher.
|
||||
selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())}
|
||||
if config.Cipher() != packet.CipherAES128 {
|
||||
selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128))
|
||||
}
|
||||
|
||||
// And for DefaultMode.
|
||||
selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())}
|
||||
if config.AEAD().Mode() != packet.AEADModeEAX {
|
||||
selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX))
|
||||
}
|
||||
|
||||
// User ID binding signature
|
||||
err = selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate an encryption subkey
|
||||
subPrivRaw, err := newDecrypter(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
|
||||
sub.IsSubkey = true
|
||||
sub.PublicKey.IsSubkey = true
|
||||
if config != nil && config.V5Keys {
|
||||
sub.UpgradeToV5()
|
||||
}
|
||||
|
||||
// NOTE: No KeyLifetimeSecs here, but we will not return this subkey in EncryptionKey()
|
||||
// if the primary/master key has expired.
|
||||
subKey := Subkey{
|
||||
PublicKey: &sub.PublicKey,
|
||||
PrivateKey: sub,
|
||||
Sig: &packet.Signature{
|
||||
Version: primary.PublicKey.Version,
|
||||
CreationTime: creationTime,
|
||||
SigType: packet.SigTypeSubkeyBinding,
|
||||
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
FlagsValid: true,
|
||||
FlagEncryptStorage: true,
|
||||
FlagEncryptCommunications: true,
|
||||
IssuerKeyId: &primary.PublicKey.KeyId,
|
||||
},
|
||||
}
|
||||
|
||||
// Subkey binding signature
|
||||
err = subKey.Sig.SignKey(subKey.PublicKey, primary, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Entity{
|
||||
PrimaryKey: &primary.PublicKey,
|
||||
PrivateKey: primary,
|
||||
Identities: map[string]*Identity{
|
||||
uid.Id: &Identity{
|
||||
Name: uid.Id,
|
||||
UserId: uid,
|
||||
SelfSignature: selfSignature,
|
||||
Signatures: []*packet.Signature{selfSignature},
|
||||
},
|
||||
},
|
||||
Subkeys: []Subkey{subKey},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AddSigningSubkey adds a signing keypair as a subkey to the Entity.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) AddSigningSubkey(config *packet.Config) error {
|
||||
creationTime := config.Now()
|
||||
keyLifetimeSecs := config.KeyLifetime()
|
||||
|
||||
subPrivRaw, err := newSigner(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
|
||||
|
||||
subkey := Subkey{
|
||||
PublicKey: &sub.PublicKey,
|
||||
PrivateKey: sub,
|
||||
Sig: &packet.Signature{
|
||||
Version: e.PrimaryKey.Version,
|
||||
CreationTime: creationTime,
|
||||
KeyLifetimeSecs: &keyLifetimeSecs,
|
||||
SigType: packet.SigTypeSubkeyBinding,
|
||||
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
FlagsValid: true,
|
||||
FlagSign: true,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
EmbeddedSignature: &packet.Signature{
|
||||
Version: e.PrimaryKey.Version,
|
||||
CreationTime: creationTime,
|
||||
SigType: packet.SigTypePrimaryKeyBinding,
|
||||
PubKeyAlgo: sub.PublicKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
},
|
||||
},
|
||||
}
|
||||
if config != nil && config.V5Keys {
|
||||
subkey.PublicKey.UpgradeToV5()
|
||||
}
|
||||
|
||||
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subkey.PublicKey.IsSubkey = true
|
||||
subkey.PrivateKey.IsSubkey = true
|
||||
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subkey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
|
||||
creationTime := config.Now()
|
||||
keyLifetimeSecs := config.KeyLifetime()
|
||||
|
||||
subPrivRaw, err := newDecrypter(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
|
||||
|
||||
subkey := Subkey{
|
||||
PublicKey: &sub.PublicKey,
|
||||
PrivateKey: sub,
|
||||
Sig: &packet.Signature{
|
||||
Version: e.PrimaryKey.Version,
|
||||
CreationTime: creationTime,
|
||||
KeyLifetimeSecs: &keyLifetimeSecs,
|
||||
SigType: packet.SigTypeSubkeyBinding,
|
||||
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
FlagsValid: true,
|
||||
FlagEncryptStorage: true,
|
||||
FlagEncryptCommunications: true,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
},
|
||||
}
|
||||
if config != nil && config.V5Keys {
|
||||
subkey.PublicKey.UpgradeToV5()
|
||||
}
|
||||
|
||||
subkey.PublicKey.IsSubkey = true
|
||||
subkey.PrivateKey.IsSubkey = true
|
||||
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subkey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates a signing key
|
||||
func newSigner(config *packet.Config) (signer crypto.Signer, err error) {
|
||||
switch config.PublicKeyAlgorithm() {
|
||||
case packet.PubKeyAlgoRSA:
|
||||
bits := config.RSAModulusBits()
|
||||
if bits < 1024 {
|
||||
return nil, errors.InvalidArgumentError("bits must be >= 1024")
|
||||
}
|
||||
if config != nil && len(config.RSAPrimes) >= 2 {
|
||||
primes := config.RSAPrimes[0:2]
|
||||
config.RSAPrimes = config.RSAPrimes[2:]
|
||||
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
|
||||
}
|
||||
return rsa.GenerateKey(config.Random(), bits)
|
||||
case packet.PubKeyAlgoEdDSA:
|
||||
_, priv, err := ed25519.GenerateKey(config.Random())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &priv, nil
|
||||
default:
|
||||
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
// Generates an encryption/decryption key
|
||||
func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
|
||||
switch config.PublicKeyAlgorithm() {
|
||||
case packet.PubKeyAlgoRSA:
|
||||
bits := config.RSAModulusBits()
|
||||
if bits < 1024 {
|
||||
return nil, errors.InvalidArgumentError("bits must be >= 1024")
|
||||
}
|
||||
if config != nil && len(config.RSAPrimes) >= 2 {
|
||||
primes := config.RSAPrimes[0:2]
|
||||
config.RSAPrimes = config.RSAPrimes[2:]
|
||||
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
|
||||
}
|
||||
return rsa.GenerateKey(config.Random(), bits)
|
||||
case packet.PubKeyAlgoEdDSA:
|
||||
fallthrough // When passing EdDSA, we generate an ECDH subkey
|
||||
case packet.PubKeyAlgoECDH:
|
||||
var kdf = ecdh.KDF{
|
||||
Hash: algorithm.SHA512,
|
||||
Cipher: algorithm.AES256,
|
||||
}
|
||||
return ecdh.X25519GenerateKey(config.Random(), kdf)
|
||||
default:
|
||||
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
var bigOne = big.NewInt(1)
|
||||
|
||||
// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the
|
||||
// given bit size, using the given random source and prepopulated primes.
|
||||
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) {
|
||||
priv := new(rsa.PrivateKey)
|
||||
priv.E = 65537
|
||||
|
||||
if nprimes < 2 {
|
||||
return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2")
|
||||
}
|
||||
|
||||
if bits < 1024 {
|
||||
return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024")
|
||||
}
|
||||
|
||||
primes := make([]*big.Int, nprimes)
|
||||
|
||||
NextSetOfPrimes:
|
||||
for {
|
||||
todo := bits
|
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// Thus each prime has the form
|
||||
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
|
||||
// And the product is:
|
||||
// P = 2^todo × α
|
||||
// where α is the product of nprimes numbers of the form 0.11...
|
||||
//
|
||||
// If α < 1/2 (which can happen for nprimes > 2), we need to
|
||||
// shift todo to compensate for lost bits: the mean value of 0.11...
|
||||
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
|
||||
// will give good results.
|
||||
if nprimes >= 7 {
|
||||
todo += (nprimes - 2) / 5
|
||||
}
|
||||
for i := 0; i < nprimes; i++ {
|
||||
var err error
|
||||
if len(prepopulatedPrimes) == 0 {
|
||||
primes[i], err = rand.Prime(random, todo/(nprimes-i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
primes[i] = prepopulatedPrimes[0]
|
||||
prepopulatedPrimes = prepopulatedPrimes[1:]
|
||||
}
|
||||
|
||||
todo -= primes[i].BitLen()
|
||||
}
|
||||
|
||||
// Make sure that primes is pairwise unequal.
|
||||
for i, prime := range primes {
|
||||
for j := 0; j < i; j++ {
|
||||
if prime.Cmp(primes[j]) == 0 {
|
||||
continue NextSetOfPrimes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n := new(big.Int).Set(bigOne)
|
||||
totient := new(big.Int).Set(bigOne)
|
||||
pminus1 := new(big.Int)
|
||||
for _, prime := range primes {
|
||||
n.Mul(n, prime)
|
||||
pminus1.Sub(prime, bigOne)
|
||||
totient.Mul(totient, pminus1)
|
||||
}
|
||||
if n.BitLen() != bits {
|
||||
// This should never happen for nprimes == 2 because
|
||||
// crypto/rand should set the top two bits in each prime.
|
||||
// For nprimes > 2 we hope it does not happen often.
|
||||
continue NextSetOfPrimes
|
||||
}
|
||||
|
||||
priv.D = new(big.Int)
|
||||
e := big.NewInt(int64(priv.E))
|
||||
ok := priv.D.ModInverse(e, totient)
|
||||
|
||||
if ok != nil {
|
||||
priv.Primes = primes
|
||||
priv.N = n
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
priv.Precompute()
|
||||
return priv, nil
|
||||
}
|
707
vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
generated
vendored
707
vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
generated
vendored
|
@ -1,707 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package openpgp
|
||||
|
||||
import (
|
||||
goerrors "errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// PublicKeyType is the armor type for a PGP public key.
|
||||
var PublicKeyType = "PGP PUBLIC KEY BLOCK"
|
||||
|
||||
// PrivateKeyType is the armor type for a PGP private key.
|
||||
var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
|
||||
|
||||
// An Entity represents the components of an OpenPGP key: a primary public key
|
||||
// (which must be a signing key), one or more identities claimed by that key,
|
||||
// and zero or more subkeys, which may be encryption keys.
|
||||
type Entity struct {
|
||||
PrimaryKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Identities map[string]*Identity // indexed by Identity.Name
|
||||
Revocations []*packet.Signature
|
||||
Subkeys []Subkey
|
||||
}
|
||||
|
||||
// An Identity represents an identity claimed by an Entity and zero or more
|
||||
// assertions by other entities about that claim.
|
||||
type Identity struct {
|
||||
Name string // by convention, has the form "Full Name (comment) <email@example.com>"
|
||||
UserId *packet.UserId
|
||||
SelfSignature *packet.Signature
|
||||
Signatures []*packet.Signature
|
||||
}
|
||||
|
||||
// A Subkey is an additional public key in an Entity. Subkeys can be used for
|
||||
// encryption.
|
||||
type Subkey struct {
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Sig *packet.Signature
|
||||
}
|
||||
|
||||
// A Key identifies a specific public key in an Entity. This is either the
|
||||
// Entity's primary key or a subkey.
|
||||
type Key struct {
|
||||
Entity *Entity
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
SelfSignature *packet.Signature
|
||||
}
|
||||
|
||||
// A KeyRing provides access to public and private keys.
|
||||
type KeyRing interface {
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
KeysById(id uint64) []Key
|
||||
// KeysByIdAndUsage returns the set of keys with the given id
|
||||
// that also meet the key usage given by requiredUsage.
|
||||
// The requiredUsage is expressed as the bitwise-OR of
|
||||
// packet.KeyFlag* values.
|
||||
KeysByIdUsage(id uint64, requiredUsage byte) []Key
|
||||
// DecryptionKeys returns all private keys that are valid for
|
||||
// decryption.
|
||||
DecryptionKeys() []Key
|
||||
}
|
||||
|
||||
// PrimaryIdentity returns the Identity marked as primary or the first identity
|
||||
// if none are so marked.
|
||||
func (e *Entity) PrimaryIdentity() *Identity {
|
||||
var firstIdentity *Identity
|
||||
for _, ident := range e.Identities {
|
||||
if firstIdentity == nil {
|
||||
firstIdentity = ident
|
||||
}
|
||||
if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
return ident
|
||||
}
|
||||
}
|
||||
return firstIdentity
|
||||
}
|
||||
|
||||
// EncryptionKey returns the best candidate Key for encrypting a message to the
|
||||
// given Entity.
|
||||
func (e *Entity) EncryptionKey(now time.Time) (Key, bool) {
|
||||
// Fail to find any encryption key if the primary key has expired.
|
||||
i := e.PrimaryIdentity()
|
||||
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now)
|
||||
if primaryKeyExpired {
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// Iterate the keys to find the newest, unexpired one
|
||||
candidateSubkey := -1
|
||||
var maxTime time.Time
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagEncryptCommunications &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
|
||||
candidateSubkey = i
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
|
||||
}
|
||||
|
||||
// If we don't have any candidate subkeys for encryption and
|
||||
// the primary key doesn't have any usage metadata then we
|
||||
// assume that the primary key is ok. Or, if the primary key is
|
||||
// marked as ok to encrypt with, then we can obviously use it.
|
||||
// Also, check expiry again just to be safe.
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !primaryKeyExpired {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
}
|
||||
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// SigningKey return the best candidate Key for signing a message with this
|
||||
// Entity.
|
||||
func (e *Entity) SigningKey(now time.Time) (Key, bool) {
|
||||
return e.SigningKeyById(now, 0)
|
||||
}
|
||||
|
||||
// SigningKeyById return the Key for signing a message with this
|
||||
// Entity and keyID.
|
||||
func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) {
|
||||
// Fail to find any signing key if the primary key has expired.
|
||||
i := e.PrimaryIdentity()
|
||||
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now)
|
||||
if primaryKeyExpired {
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// Iterate the keys to find the newest, unexpired one
|
||||
candidateSubkey := -1
|
||||
var maxTime time.Time
|
||||
for idx, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagSign &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanSign() &&
|
||||
!subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) &&
|
||||
(id == 0 || subkey.PrivateKey.KeyId == id) {
|
||||
candidateSubkey = idx
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
|
||||
}
|
||||
|
||||
// If we have no candidate subkey then we assume that it's ok to sign
|
||||
// with the primary key. Or, if the primary key is marked as ok to
|
||||
// sign with, then we can use it. Also, check expiry again just to be safe.
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanSign() && !primaryKeyExpired &&
|
||||
(id == 0 || e.PrivateKey.KeyId == id) {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
}
|
||||
|
||||
// No keys with a valid Signing Flag or no keys matched the id passed in
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// An EntityList contains one or more Entities.
|
||||
type EntityList []*Entity
|
||||
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
func (el EntityList) KeysById(id uint64) (keys []Key) {
|
||||
for _, e := range el {
|
||||
if e.PrimaryKey.KeyId == id {
|
||||
var selfSig *packet.Signature
|
||||
for _, ident := range e.Identities {
|
||||
if selfSig == nil {
|
||||
selfSig = ident.SelfSignature
|
||||
} else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
selfSig = ident.SelfSignature
|
||||
break
|
||||
}
|
||||
}
|
||||
keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig})
|
||||
}
|
||||
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PublicKey.KeyId == id {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeysByIdAndUsage returns the set of keys with the given id that also meet
|
||||
// the key usage given by requiredUsage. The requiredUsage is expressed as
|
||||
// the bitwise-OR of packet.KeyFlag* values.
|
||||
func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
|
||||
for _, key := range el.KeysById(id) {
|
||||
if len(key.Entity.Revocations) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if key.SelfSignature.RevocationReason != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if key.SelfSignature.FlagsValid && requiredUsage != 0 {
|
||||
var usage byte
|
||||
if key.SelfSignature.FlagCertify {
|
||||
usage |= packet.KeyFlagCertify
|
||||
}
|
||||
if key.SelfSignature.FlagSign {
|
||||
usage |= packet.KeyFlagSign
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptCommunications {
|
||||
usage |= packet.KeyFlagEncryptCommunications
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptStorage {
|
||||
usage |= packet.KeyFlagEncryptStorage
|
||||
}
|
||||
if usage&requiredUsage != requiredUsage {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptionKeys returns all private keys that are valid for decryption.
|
||||
func (el EntityList) DecryptionKeys() (keys []Key) {
|
||||
for _, e := range el {
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file.
|
||||
func ReadArmoredKeyRing(r io.Reader) (EntityList, error) {
|
||||
block, err := armor.Decode(r)
|
||||
if err == io.EOF {
|
||||
return nil, errors.InvalidArgumentError("no armored data found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block.Type != PublicKeyType && block.Type != PrivateKeyType {
|
||||
return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type)
|
||||
}
|
||||
|
||||
return ReadKeyRing(block.Body)
|
||||
}
|
||||
|
||||
// ReadKeyRing reads one or more public/private keys. Unsupported keys are
|
||||
// ignored as long as at least a single valid key is found.
|
||||
func ReadKeyRing(r io.Reader) (el EntityList, err error) {
|
||||
packets := packet.NewReader(r)
|
||||
var lastUnsupportedError error
|
||||
|
||||
for {
|
||||
var e *Entity
|
||||
e, err = ReadEntity(packets)
|
||||
if err != nil {
|
||||
// TODO: warn about skipped unsupported/unreadable keys
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
} else if _, ok := err.(errors.StructuralError); ok {
|
||||
// Skip unreadable, badly-formatted keys
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
el = nil
|
||||
break
|
||||
}
|
||||
} else {
|
||||
el = append(el, e)
|
||||
}
|
||||
}
|
||||
|
||||
if len(el) == 0 && err == nil {
|
||||
err = lastUnsupportedError
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readToNextPublicKey reads packets until the start of the entity and leaves
|
||||
// the first packet of the new entity in the Reader.
|
||||
func readToNextPublicKey(packets *packet.Reader) (err error) {
|
||||
var p packet.Packet
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey {
|
||||
packets.Unread(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEntity reads an entity (public key, identities, subkeys etc) from the
|
||||
// given Reader.
|
||||
func ReadEntity(packets *packet.Reader) (*Entity, error) {
|
||||
e := new(Entity)
|
||||
e.Identities = make(map[string]*Identity)
|
||||
|
||||
p, err := packets.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok {
|
||||
if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
|
||||
packets.Unread(p)
|
||||
return nil, errors.StructuralError("first packet was not a public/private key")
|
||||
}
|
||||
e.PrimaryKey = &e.PrivateKey.PublicKey
|
||||
}
|
||||
|
||||
if !e.PrimaryKey.PubKeyAlgo.CanSign() {
|
||||
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
||||
}
|
||||
|
||||
var revocations []*packet.Signature
|
||||
EachPacket:
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pkt := p.(type) {
|
||||
case *packet.UserId:
|
||||
if err := addUserID(e, packets, pkt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.Signature:
|
||||
if pkt.SigType == packet.SigTypeKeyRevocation {
|
||||
revocations = append(revocations, pkt)
|
||||
} else if pkt.SigType == packet.SigTypeDirectSignature {
|
||||
// TODO: RFC4880 5.2.1 permits signatures
|
||||
// directly on keys (eg. to bind additional
|
||||
// revocation keys).
|
||||
}
|
||||
// Else, ignoring the signature as it does not follow anything
|
||||
// we would know to attach it to.
|
||||
case *packet.PrivateKey:
|
||||
if pkt.IsSubkey == false {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, &pkt.PublicKey, pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.PublicKey:
|
||||
if pkt.IsSubkey == false {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, pkt, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
// we ignore unknown packets
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Identities) == 0 {
|
||||
return nil, errors.StructuralError("entity without any identities")
|
||||
}
|
||||
|
||||
for _, revocation := range revocations {
|
||||
err = e.PrimaryKey.VerifyRevocationSignature(revocation)
|
||||
if err == nil {
|
||||
e.Revocations = append(e.Revocations, revocation)
|
||||
} else {
|
||||
// TODO: RFC 4880 5.2.3.15 defines revocation keys.
|
||||
return nil, errors.StructuralError("revocation signature signed by alternate key")
|
||||
}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
|
||||
// Make a new Identity object, that we might wind up throwing away.
|
||||
// We'll only add it if we get a valid self-signature over this
|
||||
// userID.
|
||||
identity := new(Identity)
|
||||
identity.Name = pkt.Id
|
||||
identity.UserId = pkt
|
||||
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
|
||||
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.CheckKeyIdOrFingerprint(e.PrimaryKey) {
|
||||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
|
||||
return errors.StructuralError("user ID self-signature invalid: " + err.Error())
|
||||
}
|
||||
if identity.SelfSignature == nil || sig.CreationTime.After(identity.SelfSignature.CreationTime) {
|
||||
identity.SelfSignature = sig
|
||||
}
|
||||
identity.Signatures = append(identity.Signatures, sig)
|
||||
e.Identities[pkt.Id] = identity
|
||||
} else {
|
||||
identity.Signatures = append(identity.Signatures, sig)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
|
||||
var subKey Subkey
|
||||
subKey.PublicKey = pub
|
||||
subKey.PrivateKey = priv
|
||||
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
|
||||
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
|
||||
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
switch sig.SigType {
|
||||
case packet.SigTypeSubkeyRevocation:
|
||||
subKey.Sig = sig
|
||||
case packet.SigTypeSubkeyBinding:
|
||||
if shouldReplaceSubkeySig(subKey.Sig, sig) {
|
||||
subKey.Sig = sig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if subKey.Sig == nil {
|
||||
return errors.StructuralError("subkey packet not followed by signature")
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool {
|
||||
if potentialNewSig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if existingSig == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if existingSig.SigType == packet.SigTypeSubkeyRevocation {
|
||||
return false // never override a revocation signature
|
||||
}
|
||||
|
||||
return potentialNewSig.CreationTime.After(existingSig.CreationTime)
|
||||
}
|
||||
|
||||
// SerializePrivate serializes an Entity, including private key material, but
|
||||
// excluding signatures from other entities, to the given Writer.
|
||||
// Identities and subkeys are re-signed in case they changed since NewEntry.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
|
||||
if e.PrivateKey.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy private key cannot re-sign identities")
|
||||
}
|
||||
return e.serializePrivate(w, config, true)
|
||||
}
|
||||
|
||||
// SerializePrivateWithoutSigning serializes an Entity, including private key
|
||||
// material, but excluding signatures from other entities, to the given Writer.
|
||||
// Self-signatures of identities and subkeys are not re-signed. This is useful
|
||||
// when serializing GNU dummy keys, among other things.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SerializePrivateWithoutSigning(w io.Writer, config *packet.Config) (err error) {
|
||||
return e.serializePrivate(w, config, false)
|
||||
}
|
||||
|
||||
func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign bool) (err error) {
|
||||
if e.PrivateKey == nil {
|
||||
return goerrors.New("openpgp: private key is missing")
|
||||
}
|
||||
err = e.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reSign {
|
||||
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = ident.SelfSignature.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reSign {
|
||||
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if subkey.Sig.EmbeddedSignature != nil {
|
||||
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey,
|
||||
subkey.PrivateKey, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the public part of the given Entity to w, including
|
||||
// signatures from other entities. No private key material will be output.
|
||||
func (e *Entity) Serialize(w io.Writer) error {
|
||||
err := e.PrimaryKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sig := range ident.Signatures {
|
||||
err = sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PublicKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignIdentity adds a signature to e, from signer, attesting that identity is
|
||||
// associated with e. The provided identity must already be an element of
|
||||
// e.Identities and the private key of signer must have been decrypted if
|
||||
// necessary.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error {
|
||||
if signer.PrivateKey == nil {
|
||||
return errors.InvalidArgumentError("signing Entity must have a private key")
|
||||
}
|
||||
if signer.PrivateKey.Encrypted {
|
||||
return errors.InvalidArgumentError("signing Entity's private key must be decrypted")
|
||||
}
|
||||
ident, ok := e.Identities[identity]
|
||||
if !ok {
|
||||
return errors.InvalidArgumentError("given identity string not found in Entity")
|
||||
}
|
||||
|
||||
sig := &packet.Signature{
|
||||
Version: signer.PrivateKey.Version,
|
||||
SigType: packet.SigTypeGenericCert,
|
||||
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
CreationTime: config.Now(),
|
||||
IssuerKeyId: &signer.PrivateKey.KeyId,
|
||||
}
|
||||
if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
ident.Signatures = append(ident.Signatures, sig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeKey generates a key revocation signature (packet.SigTypeKeyRevocation) with the
|
||||
// specified reason code and text (RFC4880 section-5.2.3.23).
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) RevokeKey(reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
|
||||
reasonCode := uint8(reason)
|
||||
revSig := &packet.Signature{
|
||||
Version: e.PrimaryKey.Version,
|
||||
CreationTime: config.Now(),
|
||||
SigType: packet.SigTypeKeyRevocation,
|
||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||
Hash: config.Hash(),
|
||||
RevocationReason: &reasonCode,
|
||||
RevocationReasonText: reasonText,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
}
|
||||
|
||||
if err := revSig.RevokeKey(e.PrimaryKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Revocations = append(e.Revocations, revSig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeSubkey generates a subkey revocation signature (packet.SigTypeSubkeyRevocation) for
|
||||
// a subkey with the specified reason code and text (RFC4880 section-5.2.3.23).
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
|
||||
if err := e.PrimaryKey.VerifyKeySignature(sk.PublicKey, sk.Sig); err != nil {
|
||||
return errors.InvalidArgumentError("given subkey is not associated with this key")
|
||||
}
|
||||
|
||||
reasonCode := uint8(reason)
|
||||
revSig := &packet.Signature{
|
||||
Version: e.PrimaryKey.Version,
|
||||
CreationTime: config.Now(),
|
||||
SigType: packet.SigTypeSubkeyRevocation,
|
||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||
Hash: config.Hash(),
|
||||
RevocationReason: &reasonCode,
|
||||
RevocationReasonText: reasonText,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
}
|
||||
|
||||
if err := revSig.RevokeKey(sk.PublicKey, e.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sk.Sig = revSig
|
||||
return nil
|
||||
}
|
336
vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go
generated
vendored
336
vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.go
generated
vendored
|
@ -1,336 +0,0 @@
|
|||
package openpgp
|
||||
|
||||
const expiringKeyHex = "c6c04d0451d0c680010800abbb021fd03ffc4e96618901180c3fdcb060ee69eeead97b91256d11420d80b5f1b51930248044130bd300605cf8a05b7a40d3d8cfb0a910be2e3db50dcd50a9c54064c2a5550801daa834ff4480b33d3d3ca495ff8a4e84a886977d17d998f881241a874083d8b995beab555b6d22b8a4817ab17ac3e7304f7d4d2c05c495fb2218348d3bc13651db1d92732e368a9dd7dcefa6eddff30b94706a9aaee47e9d39321460b740c59c6fc3c2fd8ab6c0fb868cb87c0051f0321301fe0f0e1820b15e7fb7063395769b525005c7e30a7ce85984f5cac00504e7b4fdc45d74958de8388436fd5c7ba9ea121f1c851b5911dd1b47a14d81a09e92ef37721e2325b6790011010001cd00c2c07b041001080025050251d0c680050900278d00060b09070803020415080a0203160201021901021b03021e01000a0910e7b484133a890a35ae4b0800a1beb82e7f28eaf5273d6af9d3391314f6280b2b624eaca2851f89a9ebcaf80ac589ebd509f168bc4322106ca2e2ce77a76e071a3c7444787d65216b5f05e82c77928860b92aace3b7d0327db59492f422eb9dfab7249266d37429870b091a98aba8724c2259ebf8f85093f21255eafa75aa841e31d94f2ac891b9755fed455e539044ee69fc47950b80e003fc9f298d695660f28329eaa38037c367efde1727458e514faf990d439a21461b719edaddf9296d3d0647b43ca56cb8dbf63b4fcf8b9968e7928c463470fab3b98e44d0d95645062f94b2d04fe56bd52822b71934db8ce845622c40b92fcbe765a142e7f38b61a6aa9606c8e8858dcd3b6eb1894acec04d0451d1f06b01080088bea67444e1789390e7c0335c86775502d58ec783d99c8ef4e06de235ed3dd4b0467f6f358d818c7d8989d43ec6d69fcbc8c32632d5a1b605e3fa8e41d695fcdcaa535936cd0157f9040dce362519803b908eafe838bb13216c885c6f93e9e8d5745607f0d062322085d6bdc760969149a8ff8dd9f5c18d9bfe2e6f63a06e17694cf1f67587c6fb70e9aebf90ffc528ca3b615ac7c9d4a21ea4f7c06f2e98fbbd90a859b8608bf9ea638e3a54289ce44c283110d0c45fa458de6251cd6e7baf71f80f12c8978340490fd90c92b81736ae902ed958e478dceae2835953d189c45d182aff02ea2be61b81d8e94430f041d638647b43e2fcb45fd512fbf5068b810011010001c2c06504180108000f050251d1f06b050900081095021b0c000a0910e7b484133a890a35e63407fe2ec88d6d1e6c9ce7553ece0cb2524747217bad29f251d33df84599ffcc900141a355abd62126800744068a5e05dc167056aa9205273dc7765a2ed49db15c2a83b8d6e6429c902136f1e12229086c1c10c0053242c2a4ae1930db58163387a48cad64607ff2153c320e42843dec28e3fce90e7399d63ac0affa2fee1f0adc0953c89eb3f46ef1d6c04328ed13b491669d5120a3782e3ffb7c69575fb77eebd108794f4dda9d34be2bae57e8e59ec8ebfda2f6f06104b2321be408ea146e2db482b00c5055c8618de36ac9716f80da2617e225556d0fce61b01c8cea2d1e0ea982c31711060ca370f2739366e1e708f38405d784b49d16a26cf62d152eae734327cec04d0451d1f07b010800d5af91c5e7c2fd8951c8d254eab0c97cdcb66822f868b79b78c366255059a68fd74ebca9adb9b970cd9e586690e6e0756705432306878c897b10a4b4ca0005966f99ac8fa4e6f9caf54bf8e53844544beee9872a7ac64c119cf1393d96e674254b661f61ee975633d0e8a8672531edb6bb8e211204e7754a9efa802342118eee850beea742bac95a3f706cc2024cf6037a308bb68162b2f53b9a6346a96e6d31871a2456186e24a1c7a82b82ac04afdfd57cd7fb9ba77a9c760d40b76a170f7be525e5fb6a9848cc726e806187710d9b190387df28700f321f988a392899f93815cc937f309129eb94d5299c5547cb2c085898e6639496e70d746c9d3fb9881d0011010001c2c06504180108000f050251d1f07b050900266305021b0c000a0910e7b484133a890a35bff207fd10dfe8c4a6ea1dd30568012b6fd6891a763c87ad0f7a1d112aad9e8e3239378a3b85588c235865bac2e614348cb4f216d7217f53b3ef48c192e0a4d31d64d7bfa5faccf21155965fa156e887056db644a05ad08a85cc6152d1377d9e37b46f4ff462bbe68ace2dc586ef90070314576c985d8037c2ba63f0a7dc17a62e15bd77e88bc61d9d00858979709f12304264a4cf4225c5cf86f12c8e19486cb9cdcc69f18f027e5f16f4ca8b50e28b3115eaff3a345acd21f624aef81f6ede515c1b55b26b84c1e32264754eab672d5489b287e7277ea855e0a5ff2aa9e8b8c76d579a964ec225255f4d57bf66639ccb34b64798846943e162a41096a7002ca21c7f56"
|
||||
const subkeyUsageHex = "988d04533a52bc010400d26af43085558f65b9e7dbc90cb9238015259aed5e954637adcfa2181548b2d0b60c65f1f42ec5081cbf1bc0a8aa4900acfb77070837c58f26012fbce297d70afe96e759ad63531f0037538e70dbf8e384569b9720d99d8eb39d8d0a2947233ed242436cb6ac7dfe74123354b3d0119b5c235d3dd9c9d6c004f8ffaf67ad8583001101000188b7041f010200210502533b8552170c8001ce094aa433f7040bb2ddf0be3893cb843d0fe70c020700000a0910a42704b92866382aa98404009d63d916a27543da4221c60087c33f1c44bec9998c5438018ed370cca4962876c748e94b73eb39c58eb698063f3fd6346d58dd2a11c0247934c4a9d71f24754f7468f96fb24c3e791dd2392b62f626148ad724189498cbf993db2df7c0cdc2d677c35da0f16cb16c9ce7c33b4de65a4a91b1d21a130ae9cc26067718910ef8e2b417556d627261203c756d627261407379642e65642e61753e88b80413010200220502533a52bc021b03060b090807030206150802090a0b0416020301021e01021780000a0910a42704b92866382a47840400c0c2bd04f5fca586de408b395b3c280a278259c93eaaa8b79a53b97003f8ed502a8a00446dd9947fb462677e4fcac0dac2f0701847d15130aadb6cd9e0705ea0cf5f92f129136c7be21a718d46c8e641eb7f044f2adae573e11ae423a0a9ca51324f03a8a2f34b91fa40c3cc764bee4dccadedb54c768ba0469b683ea53f1c29b88d04533a52bc01040099c92a5d6f8b744224da27bc2369127c35269b58bec179de6bbc038f749344222f85a31933224f26b70243c4e4b2d242f0c4777eaef7b5502f9dad6d8bf3aaeb471210674b74de2d7078af497d55f5cdad97c7bedfbc1b41e8065a97c9c3d344b21fc81d27723af8e374bc595da26ea242dccb6ae497be26eea57e563ed517e90011010001889f0418010200090502533a52bc021b0c000a0910a42704b92866382afa1403ff70284c2de8a043ff51d8d29772602fa98009b7861c540535f874f2c230af8caf5638151a636b21f8255003997ccd29747fdd06777bb24f9593bd7d98a3e887689bf902f999915fcc94625ae487e5d13e6616f89090ebc4fdc7eb5cad8943e4056995bb61c6af37f8043016876a958ec7ebf39c43d20d53b7f546cfa83e8d2604b88d04533b8283010400c0b529316dbdf58b4c54461e7e669dc11c09eb7f73819f178ccd4177b9182b91d138605fcf1e463262fabefa73f94a52b5e15d1904635541c7ea540f07050ce0fb51b73e6f88644cec86e91107c957a114f69554548a85295d2b70bd0b203992f76eb5d493d86d9eabcaa7ef3fc7db7e458438db3fcdb0ca1cc97c638439a9170011010001889f0418010200090502533b8283021b0c000a0910a42704b92866382adc6d0400cfff6258485a21675adb7a811c3e19ebca18851533f75a7ba317950b9997fda8d1a4c8c76505c08c04b6c2cc31dc704d33da36a21273f2b388a1a706f7c3378b66d887197a525936ed9a69acb57fe7f718133da85ec742001c5d1864e9c6c8ea1b94f1c3759cebfd93b18606066c063a63be86085b7e37bdbc65f9a915bf084bb901a204533b85cd110400aed3d2c52af2b38b5b67904b0ef73d6dd7aef86adb770e2b153cd22489654dcc91730892087bb9856ae2d9f7ed1eb48f214243fe86bfe87b349ebd7c30e630e49c07b21fdabf78b7a95c8b7f969e97e3d33f2e074c63552ba64a2ded7badc05ce0ea2be6d53485f6900c7860c7aa76560376ce963d7271b9b54638a4028b573f00a0d8854bfcdb04986141568046202192263b9b67350400aaa1049dbc7943141ef590a70dcb028d730371d92ea4863de715f7f0f16d168bd3dc266c2450457d46dcbbf0b071547e5fbee7700a820c3750b236335d8d5848adb3c0da010e998908dfd93d961480084f3aea20b247034f8988eccb5546efaa35a92d0451df3aaf1aee5aa36a4c4d462c760ecd9cebcabfbe1412b1f21450f203fd126687cd486496e971a87fd9e1a8a765fe654baa219a6871ab97768596ab05c26c1aeea8f1a2c72395a58dbc12ef9640d2b95784e974a4d2d5a9b17c25fedacfe551bda52602de8f6d2e48443f5dd1a2a2a8e6a5e70ecdb88cd6e766ad9745c7ee91d78cc55c3d06536b49c3fee6c3d0b6ff0fb2bf13a314f57c953b8f4d93bf88e70418010200090502533b85cd021b0200520910a42704b92866382a47200419110200060502533b85cd000a091042ce2c64bc0ba99214b2009e26b26852c8b13b10c35768e40e78fbbb48bd084100a0c79d9ea0844fa5853dd3c85ff3ecae6f2c9dd6c557aa04008bbbc964cd65b9b8299d4ebf31f41cc7264b8cf33a00e82c5af022331fac79efc9563a822497ba012953cefe2629f1242fcdcb911dbb2315985bab060bfd58261ace3c654bdbbe2e8ed27a46e836490145c86dc7bae15c011f7e1ffc33730109b9338cd9f483e7cef3d2f396aab5bd80efb6646d7e778270ee99d934d187dd98"
|
||||
const revokedKeyHex = "988d045331ce82010400c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be10011010001889f04200102000905025331d0e3021d03000a0910a401d9f09a34f7c042aa040086631196405b7e6af71026b88e98012eab44aa9849f6ef3fa930c7c9f23deaedba9db1538830f8652fb7648ec3fcade8dbcbf9eaf428e83c6cbcc272201bfe2fbb90d41963397a7c0637a1a9d9448ce695d9790db2dc95433ad7be19eb3de72dacf1d6db82c3644c13eae2a3d072b99bb341debba012c5ce4006a7d34a1f4b94b444526567205265766f6b657220283c52656727732022424d204261726973746122204b657920262530305c303e5c29203c72656740626d626172697374612e636f2e61753e88b704130102002205025331ce82021b03060b090807030206150802090a0b0416020301021e01021780000a0910a401d9f09a34f7c0019c03f75edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56889c04100102000605025331cfb5000a0910fe9645554e8266b64b4303fc084075396674fb6f778d302ac07cef6bc0b5d07b66b2004c44aef711cbac79617ef06d836b4957522d8772dd94bf41a2f4ac8b1ee6d70c57503f837445a74765a076d07b829b8111fc2a918423ddb817ead7ca2a613ef0bfb9c6b3562aec6c3cf3c75ef3031d81d95f6563e4cdcc9960bcb386c5d757b104fcca5fe11fc709df884604101102000605025331cfe7000a09107b15a67f0b3ddc0317f6009e360beea58f29c1d963a22b962b80788c3fa6c84e009d148cfde6b351469b8eae91187eff07ad9d08fcaab88d045331ce820104009f25e20a42b904f3fa555530fe5c46737cf7bd076c35a2a0d22b11f7e0b61a69320b768f4a80fe13980ce380d1cfc4a0cd8fbe2d2e2ef85416668b77208baa65bf973fe8e500e78cc310d7c8705cdb34328bf80e24f0385fce5845c33bc7943cf6b11b02348a23da0bf6428e57c05135f2dc6bd7c1ce325d666d5a5fd2fd5e410011010001889f04180102000905025331ce82021b0c000a0910a401d9f09a34f7c0418003fe34feafcbeaef348a800a0d908a7a6809cc7304017d820f70f0474d5e23cb17e38b67dc6dca282c6ca00961f4ec9edf2738d0f087b1d81e4871ef08e1798010863afb4eac4c44a376cb343be929c5be66a78cfd4456ae9ec6a99d97f4e1c3ff3583351db2147a65c0acef5c003fb544ab3a2e2dc4d43646f58b811a6c3a369d1f"
|
||||
const revokedSubkeyHex = "988d04533121f6010400aefc803a3e4bb1a61c86e8a86d2726c6a43e0079e9f2713f1fa017e9854c83877f4aced8e331d675c67ea83ddab80aacbfa0b9040bb12d96f5a3d6be09455e2a76546cbd21677537db941cab710216b6d24ec277ee0bd65b910f416737ed120f6b93a9d3b306245c8cfd8394606fdb462e5cf43c551438d2864506c63367fc890011010001b41d416c696365203c616c69636540626d626172697374612e636f2e61753e88bb041301020025021b03060b090807030206150802090a0b0416020301021e01021780050253312798021901000a09104ef7e4beccde97f015a803ff5448437780f63263b0df8442a995e7f76c221351a51edd06f2063d8166cf3157aada4923dfc44aa0f2a6a4da5cf83b7fe722ba8ab416c976e77c6b5682e7f1069026673bd0de56ba06fd5d7a9f177607f277d9b55ff940a638c3e68525c67517e2b3d976899b93ca267f705b3e5efad7d61220e96b618a4497eab8d04403d23f8846041011020006050253312910000a09107b15a67f0b3ddc03d96e009f50b6365d86c4be5d5e9d0ea42d5e56f5794c617700a0ab274e19c2827780016d23417ce89e0a2c0d987d889c04100102000605025331cf7a000a0910a401d9f09a34f7c0ee970400aca292f213041c9f3b3fc49148cbda9d84afee6183c8dd6c5ff2600b29482db5fecd4303797be1ee6d544a20a858080fec43412061c9a71fae4039fd58013b4ae341273e6c66ad4c7cdd9e68245bedb260562e7b166f2461a1032f2b38c0e0e5715fb3d1656979e052b55ca827a76f872b78a9fdae64bc298170bfcebedc1271b41a416c696365203c616c696365407379646973702e6f722e61753e88b804130102002205025331278b021b03060b090807030206150802090a0b0416020301021e01021780000a09104ef7e4beccde97f06a7003fa03c3af68d272ebc1fa08aa72a03b02189c26496a2833d90450801c4e42c5b5f51ad96ce2d2c9cef4b7c02a6a2fcf1412d6a2d486098eb762f5010a201819c17fd2888aec8eda20c65a3b75744de7ee5cc8ac7bfc470cbe3cb982720405a27a3c6a8c229cfe36905f881b02ed5680f6a8f05866efb9d6c5844897e631deb949ca8846041011020006050253312910000a09107b15a67f0b3ddc0347bc009f7fa35db59147469eb6f2c5aaf6428accb138b22800a0caa2f5f0874bacc5909c652a57a31beda65eddd5889c04100102000605025331cf7a000a0910a401d9f09a34f7c0316403ff46f2a5c101256627f16384d34a38fb47a6c88ba60506843e532d91614339fccae5f884a5741e7582ffaf292ba38ee10a270a05f139bde3814b6a077e8cd2db0f105ebea2a83af70d385f13b507fac2ad93ff79d84950328bb86f3074745a8b7f9b64990fb142e2a12976e27e8d09a28dc5621f957ac49091116da410ac3cbde1b88d04533121f6010400cbd785b56905e4192e2fb62a720727d43c4fa487821203cf72138b884b78b701093243e1d8c92a0248a6c0203a5a88693da34af357499abacaf4b3309c640797d03093870a323b4b6f37865f6eaa2838148a67df4735d43a90ca87942554cdf1c4a751b1e75f9fd4ce4e97e278d6c1c7ed59d33441df7d084f3f02beb68896c70011010001889f0418010200090502533121f6021b0c000a09104ef7e4beccde97f0b98b03fc0a5ccf6a372995835a2f5da33b282a7d612c0ab2a97f59cf9fff73e9110981aac2858c41399afa29624a7fd8a0add11654e3d882c0fd199e161bdad65e5e2548f7b68a437ea64293db1246e3011cbb94dc1bcdeaf0f2539bd88ff16d95547144d97cead6a8c5927660a91e6db0d16eb36b7b49a3525b54d1644e65599b032b7eb901a204533127a0110400bd3edaa09eff9809c4edc2c2a0ebe52e53c50a19c1e49ab78e6167bf61473bb08f2050d78a5cbbc6ed66aff7b42cd503f16b4a0b99fa1609681fca9b7ce2bbb1a5b3864d6cdda4d7ef7849d156d534dea30fb0efb9e4cf8959a2b2ce623905882d5430b995a15c3b9fe92906086788b891002924f94abe139b42cbbfaaabe42f00a0b65dc1a1ad27d798adbcb5b5ad02d2688c89477b03ff4eebb6f7b15a73b96a96bed201c0e5e4ea27e4c6e2dd1005b94d4b90137a5b1cf5e01c6226c070c4cc999938101578877ee76d296b9aab8246d57049caacf489e80a3f40589cade790a020b1ac146d6f7a6241184b8c7fcde680eae3188f5dcbe846d7f7bdad34f6fcfca08413e19c1d5df83fc7c7c627d493492e009c2f52a80400a2fe82de87136fd2e8845888c4431b032ba29d9a29a804277e31002a8201fb8591a3e55c7a0d0881496caf8b9fb07544a5a4879291d0dc026a0ea9e5bd88eb4aa4947bbd694b25012e208a250d65ddc6f1eea59d3aed3b4ec15fcab85e2afaa23a40ab1ef9ce3e11e1bc1c34a0e758e7aa64deb8739276df0af7d4121f834a9b88e70418010200090502533127a0021b02005209104ef7e4beccde97f047200419110200060502533127a0000a0910dbce4ee19529437fe045009c0b32f5ead48ee8a7e98fac0dea3d3e6c0e2c552500a0ad71fadc5007cfaf842d9b7db3335a8cdad15d3d1a6404009b08e2c68fe8f3b45c1bb72a4b3278cdf3012aa0f229883ad74aa1f6000bb90b18301b2f85372ca5d6b9bf478d235b733b1b197d19ccca48e9daf8e890cb64546b4ce1b178faccfff07003c172a2d4f5ebaba9f57153955f3f61a9b80a4f5cb959908f8b211b03b7026a8a82fc612bfedd3794969bcf458c4ce92be215a1176ab88d045331d144010400a5063000c5aaf34953c1aa3bfc95045b3aab9882b9a8027fecfe2142dc6b47ba8aca667399990244d513dd0504716908c17d92c65e74219e004f7b83fc125e575dd58efec3ab6dd22e3580106998523dea42ec75bf9aa111734c82df54630bebdff20fe981cfc36c76f865eb1c2fb62c9e85bc3a6e5015a361a2eb1c8431578d0011010001889f04280102000905025331d433021d03000a09104ef7e4beccde97f02e5503ff5e0630d1b65291f4882b6d40a29da4616bb5088717d469fbcc3648b8276de04a04988b1f1b9f3e18f52265c1f8b6c85861691c1a6b8a3a25a1809a0b32ad330aec5667cb4262f4450649184e8113849b05e5ad06a316ea80c001e8e71838190339a6e48bbde30647bcf245134b9a97fa875c1d83a9862cae87ffd7e2c4ce3a1b89013d04180102000905025331d144021b0200a809104ef7e4beccde97f09d2004190102000605025331d144000a0910677815e371c2fd23522203fe22ab62b8e7a151383cea3edd3a12995693911426f8ccf125e1f6426388c0010f88d9ca7da2224aee8d1c12135998640c5e1813d55a93df472faae75bef858457248db41b4505827590aeccf6f9eb646da7f980655dd3050c6897feddddaca90676dee856d66db8923477d251712bb9b3186b4d0114daf7d6b59272b53218dd1da94a03ff64006fcbe71211e5daecd9961fba66cdb6de3f914882c58ba5beddeba7dcb950c1156d7fba18c19ea880dccc800eae335deec34e3b84ac75ffa24864f782f87815cda1c0f634b3dd2fa67cea30811d21723d21d9551fa12ccbcfa62b6d3a15d01307b99925707992556d50065505b090aadb8579083a20fe65bd2a270da9b011"
|
||||
|
||||
const missingCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Charset: UTF-8
|
||||
|
||||
mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY
|
||||
ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG
|
||||
zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54
|
||||
QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ
|
||||
QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo
|
||||
9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu
|
||||
Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/
|
||||
dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R
|
||||
JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL
|
||||
ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew
|
||||
RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW
|
||||
/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu
|
||||
yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAJcXQeP+NmuciE99YcJoffxv
|
||||
2gVLU4ZXBNHEaP0mgaJ1+tmMD089vUQAcyGRvw8jfsNsVZQIOAuRxY94aHQhIRHR
|
||||
bUzBN28ofo/AJJtfx62C15xt6fDKRV6HXYqAiygrHIpEoRLyiN69iScUsjIJeyFL
|
||||
C8wa72e8pSL6dkHoaV1N9ZH/xmrJ+k0vsgkQaAh9CzYufncDxcwkoP+aOlGtX1gP
|
||||
WwWoIbz0JwLEMPHBWvDDXQcQPQTYQyj+LGC9U6f9VZHN25E94subM1MjuT9OhN9Y
|
||||
MLfWaaIc5WyhLFyQKW2Upofn9wSFi8ubyBnv640Dfd0rVmaWv7LNTZpoZ/GbJAMA
|
||||
EQEAAYkBHwQYAQIACQUCU5ygeQIbAgAKCRDt1A0FCB6SP0zCB/sEzaVR38vpx+OQ
|
||||
MMynCBJrakiqDmUZv9xtplY7zsHSQjpd6xGflbU2n+iX99Q+nav0ETQZifNUEd4N
|
||||
1ljDGQejcTyKD6Pkg6wBL3x9/RJye7Zszazm4+toJXZ8xJ3800+BtaPoI39akYJm
|
||||
+ijzbskvN0v/j5GOFJwQO0pPRAFtdHqRs9Kf4YanxhedB4dIUblzlIJuKsxFit6N
|
||||
lgGRblagG3Vv2eBszbxzPbJjHCgVLR3RmrVezKOsZjr/2i7X+xLWIR0uD3IN1qOW
|
||||
CXQxLBizEEmSNVNxsp7KPGTLnqO3bPtqFirxS9PJLIMPTPLNBY7ZYuPNTMqVIUWF
|
||||
4artDmrG
|
||||
=7FfJ
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
const invalidCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY
|
||||
ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG
|
||||
zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54
|
||||
QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ
|
||||
QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo
|
||||
9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu
|
||||
Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/
|
||||
dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R
|
||||
JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL
|
||||
ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew
|
||||
RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW
|
||||
/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu
|
||||
yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAIINDqlj7X6jYKc6DjwrOkjQ
|
||||
UIRWbQQar0LwmNilehmt70g5DCL1SYm9q4LcgJJ2Nhxj0/5qqsYib50OSWMcKeEe
|
||||
iRXpXzv1ObpcQtI5ithp0gR53YPXBib80t3bUzomQ5UyZqAAHzMp3BKC54/vUrSK
|
||||
FeRaxDzNLrCeyI00+LHNUtwghAqHvdNcsIf8VRumK8oTm3RmDh0TyjASWYbrt9c8
|
||||
R1Um3zuoACOVy+mEIgIzsfHq0u7dwYwJB5+KeM7ZLx+HGIYdUYzHuUE1sLwVoELh
|
||||
+SHIGHI1HDicOjzqgajShuIjj5hZTyQySVprrsLKiXS6NEwHAP20+XjayJ/R3tEA
|
||||
EQEAAYkCPgQYAQIBKAUCU5ygeQIbAsBdIAQZAQIABgUCU5ygeQAKCRCpVlnFZmhO
|
||||
52RJB/9uD1MSa0wjY6tHOIgquZcP3bHBvHmrHNMw9HR2wRCMO91ZkhrpdS3ZHtgb
|
||||
u3/55etj0FdvDo1tb8P8FGSVtO5Vcwf5APM8sbbqoi8L951Q3i7qt847lfhu6sMl
|
||||
w0LWFvPTOLHrliZHItPRjOltS1WAWfr2jUYhsU9ytaDAJmvf9DujxEOsN5G1YJep
|
||||
54JCKVCkM/y585Zcnn+yxk/XwqoNQ0/iJUT9qRrZWvoeasxhl1PQcwihCwss44A+
|
||||
YXaAt3hbk+6LEQuZoYS73yR3WHj+42tfm7YxRGeubXfgCEz/brETEWXMh4pe0vCL
|
||||
bfWrmfSPq2rDegYcAybxRQz0lF8PAAoJEO3UDQUIHpI/exkH/0vQfdHA8g/N4T6E
|
||||
i6b1CUVBAkvtdJpCATZjWPhXmShOw62gkDw306vHPilL4SCvEEi4KzG72zkp6VsB
|
||||
DSRcpxCwT4mHue+duiy53/aRMtSJ+vDfiV1Vhq+3sWAck/yUtfDU9/u4eFaiNok1
|
||||
8/Gd7reyuZt5CiJnpdPpjCwelK21l2w7sHAnJF55ITXdOxI8oG3BRKufz0z5lyDY
|
||||
s2tXYmhhQIggdgelN8LbcMhWs/PBbtUr6uZlNJG2lW1yscD4aI529VjwJlCeo745
|
||||
U7pO4eF05VViUJ2mmfoivL3tkhoTUWhx8xs8xCUcCg8DoEoSIhxtOmoTPR22Z9BL
|
||||
6LCg2mg=
|
||||
=Dhm4
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
const goodCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
mI0EVUqeVwEEAMufHRrMPWK3gyvi0O0tABCs/oON9zV9KDZlr1a1M91ShCSFwCPo
|
||||
7r80PxdWVWcj0V5h50/CJYtpN3eE/mUIgW2z1uDYQF1OzrQ8ubrksfsJvpAhENom
|
||||
lTQEppv9mV8qhcM278teb7TX0pgrUHLYF5CfPdp1L957JLLXoQR/lwLVABEBAAG0
|
||||
E2dvb2Qtc2lnbmluZy1zdWJrZXmIuAQTAQIAIgUCVUqeVwIbAwYLCQgHAwIGFQgC
|
||||
CQoLBBYCAwECHgECF4AACgkQNRjL95IRWP69XQQAlH6+eyXJN4DZTLX78KGjHrsw
|
||||
6FCvxxClEPtPUjcJy/1KCRQmtLAt9PbbA78dvgzjDeZMZqRAwdjyJhjyg/fkU2OH
|
||||
7wq4ktjUu+dLcOBb+BFMEY+YjKZhf6EJuVfxoTVr5f82XNPbYHfTho9/OABKH6kv
|
||||
X70PaKZhbwnwij8Nts65AaIEVUqftREEAJ3WxZfqAX0bTDbQPf2CMT2IVMGDfhK7
|
||||
GyubOZgDFFjwUJQvHNvsrbeGLZ0xOBumLINyPO1amIfTgJNm1iiWFWfmnHReGcDl
|
||||
y5mpYG60Mb79Whdcer7CMm3AqYh/dW4g6IB02NwZMKoUHo3PXmFLxMKXnWyJ0clw
|
||||
R0LI/Qn509yXAKDh1SO20rqrBM+EAP2c5bfI98kyNwQAi3buu94qo3RR1ZbvfxgW
|
||||
CKXDVm6N99jdZGNK7FbRifXqzJJDLcXZKLnstnC4Sd3uyfyf1uFhmDLIQRryn5m+
|
||||
LBYHfDBPN3kdm7bsZDDq9GbTHiFZUfm/tChVKXWxkhpAmHhU/tH6GGzNSMXuIWSO
|
||||
aOz3Rqq0ED4NXyNKjdF9MiwD/i83S0ZBc0LmJYt4Z10jtH2B6tYdqnAK29uQaadx
|
||||
yZCX2scE09UIm32/w7pV77CKr1Cp/4OzAXS1tmFzQ+bX7DR+Gl8t4wxr57VeEMvl
|
||||
BGw4Vjh3X8//m3xynxycQU18Q1zJ6PkiMyPw2owZ/nss3hpSRKFJsxMLhW3fKmKr
|
||||
Ey2KiOcEGAECAAkFAlVKn7UCGwIAUgkQNRjL95IRWP5HIAQZEQIABgUCVUqftQAK
|
||||
CRD98VjDN10SqkWrAKDTpEY8D8HC02E/KVC5YUI01B30wgCgurpILm20kXEDCeHp
|
||||
C5pygfXw1DJrhAP+NyPJ4um/bU1I+rXaHHJYroYJs8YSweiNcwiHDQn0Engh/mVZ
|
||||
SqLHvbKh2dL/RXymC3+rjPvQf5cup9bPxNMa6WagdYBNAfzWGtkVISeaQW+cTEp/
|
||||
MtgVijRGXR/lGLGETPg2X3Afwn9N9bLMBkBprKgbBqU7lpaoPupxT61bL70=
|
||||
=vtbN
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
const revokedUserIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFsgO5EBCADhREPmcjsPkXe1z7ctvyWL0S7oa9JaoGZ9oPDHFDlQxd0qlX2e
|
||||
DZJZDg0qYvVixmaULIulApq1puEsaJCn3lHUbHlb4PYKwLEywYXM28JN91KtLsz/
|
||||
uaEX2KC5WqeP40utmzkNLq+oRX/xnRMgwbO7yUNVG2UlEa6eI+xOXO3YtLdmJMBW
|
||||
ClQ066ZnOIzEo1JxnIwha1CDBMWLLfOLrg6l8InUqaXbtEBbnaIYO6fXVXELUjkx
|
||||
nmk7t/QOk0tXCy8muH9UDqJkwDUESY2l79XwBAcx9riX8vY7vwC34pm22fAUVLCJ
|
||||
x1SJx0J8bkeNp38jKM2Zd9SUQqSbfBopQ4pPABEBAAG0I0dvbGFuZyBHb3BoZXIg
|
||||
PG5vLXJlcGx5QGdvbGFuZy5jb20+iQFUBBMBCgA+FiEE5Ik5JLcNx6l6rZfw1oFy
|
||||
9I6cUoMFAlsgO5ECGwMFCQPCZwAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ
|
||||
1oFy9I6cUoMIkwf8DNPeD23i4jRwd/pylbvxwZintZl1fSwTJW1xcOa1emXaEtX2
|
||||
depuqhP04fjlRQGfsYAQh7X9jOJxAHjTmhqFBi5sD7QvKU00cPFYbJ/JTx0B41bl
|
||||
aXnSbGhRPh63QtEZL7ACAs+shwvvojJqysx7kyVRu0EW2wqjXdHwR/SJO6nhNBa2
|
||||
DXzSiOU/SUA42mmG+5kjF8Aabq9wPwT9wjraHShEweNerNMmOqJExBOy3yFeyDpa
|
||||
XwEZFzBfOKoxFNkIaVf5GSdIUGhFECkGvBMB935khftmgR8APxdU4BE7XrXexFJU
|
||||
8RCuPXonm4WQOwTWR0vQg64pb2WKAzZ8HhwTGbQiR29sYW5nIEdvcGhlciA8cmV2
|
||||
b2tlZEBnb2xhbmcuY29tPokBNgQwAQoAIBYhBOSJOSS3Dcepeq2X8NaBcvSOnFKD
|
||||
BQJbIDv3Ah0AAAoJENaBcvSOnFKDfWMIAKhI/Tvu3h8fSUxp/gSAcduT6bC1JttG
|
||||
0lYQ5ilKB/58lBUA5CO3ZrKDKlzW3M8VEcvohVaqeTMKeoQd5rCZq8KxHn/KvN6N
|
||||
s85REfXfniCKfAbnGgVXX3kDmZ1g63pkxrFu0fDZjVDXC6vy+I0sGyI/Inro0Pzb
|
||||
tvn0QCsxjapKK15BtmSrpgHgzVqVg0cUp8vqZeKFxarYbYB2idtGRci4b9tObOK0
|
||||
BSTVFy26+I/mrFGaPrySYiy2Kz5NMEcRhjmTxJ8jSwEr2O2sUR0yjbgUAXbTxDVE
|
||||
/jg5fQZ1ACvBRQnB7LvMHcInbzjyeTM3FazkkSYQD6b97+dkWwb1iWG5AQ0EWyA7
|
||||
kQEIALkg04REDZo1JgdYV4x8HJKFS4xAYWbIva1ZPqvDNmZRUbQZR2+gpJGEwn7z
|
||||
VofGvnOYiGW56AS5j31SFf5kro1+1bZQ5iOONBng08OOo58/l1hRseIIVGB5TGSa
|
||||
PCdChKKHreJI6hS3mShxH6hdfFtiZuB45rwoaArMMsYcjaezLwKeLc396cpUwwcZ
|
||||
snLUNd1Xu5EWEF2OdFkZ2a1qYdxBvAYdQf4+1Nr+NRIx1u1NS9c8jp3PuMOkrQEi
|
||||
bNtc1v6v0Jy52mKLG4y7mC/erIkvkQBYJdxPaP7LZVaPYc3/xskcyijrJ/5ufoD8
|
||||
K71/ShtsZUXSQn9jlRaYR0EbojMAEQEAAYkBPAQYAQoAJhYhBOSJOSS3Dcepeq2X
|
||||
8NaBcvSOnFKDBQJbIDuRAhsMBQkDwmcAAAoJENaBcvSOnFKDkFMIAIt64bVZ8x7+
|
||||
TitH1bR4pgcNkaKmgKoZz6FXu80+SnbuEt2NnDyf1cLOSimSTILpwLIuv9Uft5Pb
|
||||
OraQbYt3xi9yrqdKqGLv80bxqK0NuryNkvh9yyx5WoG1iKqMj9/FjGghuPrRaT4l
|
||||
QinNAghGVkEy1+aXGFrG2DsOC1FFI51CC2WVTzZ5RwR2GpiNRfESsU1rZAUqf/2V
|
||||
yJl9bD5R4SUNy8oQmhOxi+gbhD4Ao34e4W0ilibslI/uawvCiOwlu5NGd8zv5n+U
|
||||
heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB
|
||||
7qTZOahrETw=
|
||||
=IKnw
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
const keyWithSubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mI0EWyKwKQEEALwXhKBnyaaNFeK3ljfc/qn9X/QFw+28EUfgZPHjRmHubuXLE2uR
|
||||
s3ZoSXY2z7Dkv+NyHYMt8p+X8q5fR7JvUjK2XbPyKoiJVnHINll83yl67DaWfKNL
|
||||
EjNoO0kIfbXfCkZ7EG6DL+iKtuxniGTcnGT47e+HJSqb/STpLMnWwXjBABEBAAG0
|
||||
I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQQ/
|
||||
lRafP/p9PytHbwxMvYJsOQdOOAUCWyKwKQIbAwULCQgHAwUVCgkICwUWAgMBAAIe
|
||||
AQIXgAAKCRBMvYJsOQdOOOsFBAC62mXww8XuqvYLcVOvHkWLT6mhxrQOJXnlfpn7
|
||||
2uBV9CMhoG/Ycd43NONsJrB95Apr9TDIqWnVszNbqPCuBhZQSGLdbiDKjxnCWBk0
|
||||
69qv4RNtkpOhYB7jK4s8F5oQZqId6JasT/PmJTH92mhBYhhTQr0GYFuPX2UJdkw9
|
||||
Sn9C67iNBFsisDUBBAC3A+Yo9lgCnxi/pfskyLrweYif6kIXWLAtLTsM6g/6jt7b
|
||||
wTrknuCPyTv0QKGXsAEe/cK/Xq3HvX9WfXPGIHc/X56ZIsHQ+RLowbZV/Lhok1IW
|
||||
FAuQm8axr/by80cRwFnzhfPc/ukkAq2Qyj4hLsGblu6mxeAhzcp8aqmWOO2H9QAR
|
||||
AQABiLYEKAEKACAWIQQ/lRafP/p9PytHbwxMvYJsOQdOOAUCWyK16gIdAAAKCRBM
|
||||
vYJsOQdOOB1vA/4u4uLONsE+2GVOyBsHyy7uTdkuxaR9b54A/cz6jT/tzUbeIzgx
|
||||
22neWhgvIEghnUZd0vEyK9k1wy5vbDlEo6nKzHso32N1QExGr5upRERAxweDxGOj
|
||||
7luDwNypI7QcifE64lS/JmlnunwRCdRWMKc0Fp+7jtRc5mpwyHN/Suf5RokBagQY
|
||||
AQoAIBYhBD+VFp8/+n0/K0dvDEy9gmw5B044BQJbIrA1AhsCAL8JEEy9gmw5B044
|
||||
tCAEGQEKAB0WIQSNdnkaWY6t62iX336UXbGvYdhXJwUCWyKwNQAKCRCUXbGvYdhX
|
||||
JxJSA/9fCPHP6sUtGF1o3G1a3yvOUDGr1JWcct9U+QpbCt1mZoNopCNDDQAJvDWl
|
||||
mvDgHfuogmgNJRjOMznvahbF+wpTXmB7LS0SK412gJzl1fFIpK4bgnhu0TwxNsO1
|
||||
8UkCZWqxRMgcNUn9z6XWONK8dgt5JNvHSHrwF4CxxwjL23AAtK+FA/UUoi3U4kbC
|
||||
0XnSr1Sl+mrzQi1+H7xyMe7zjqe+gGANtskqexHzwWPUJCPZ5qpIa2l8ghiUim6b
|
||||
4ymJ+N8/T8Yva1FaPEqfMzzqJr8McYFm0URioXJPvOAlRxdHPteZ0qUopt/Jawxl
|
||||
Xt6B9h1YpeLoJwjwsvbi98UTRs0jXwoY
|
||||
=3fWu
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
const keyWithSubKeyAndBadSelfSigOrder = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mI0EWyLLDQEEAOqIOpJ/ha1OYAGduu9tS3rBz5vyjbNgJO4sFveEM0mgsHQ0X9/L
|
||||
plonW+d0gRoO1dhJ8QICjDAc6+cna1DE3tEb5m6JtQ30teLZuqrR398Cf6w7NNVz
|
||||
r3lrlmnH9JaKRuXl7tZciwyovneBfZVCdtsRZjaLI1uMQCz/BToiYe3DABEBAAG0
|
||||
I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQRZ
|
||||
sixZOfQcZdW0wUqmgmdsv1O9xgUCWyLLDQIbAwULCQgHAwUVCgkICwUWAgMBAAIe
|
||||
AQIXgAAKCRCmgmdsv1O9xql2A/4pix98NxjhdsXtazA9agpAKeADf9tG4Za27Gj+
|
||||
3DCww/E4iP2X35jZimSm/30QRB6j08uGCqd9vXkkJxtOt63y/IpVOtWX6vMWSTUm
|
||||
k8xKkaYMP0/IzKNJ1qC/qYEUYpwERBKg9Z+k99E2Ql4kRHdxXUHq6OzY79H18Y+s
|
||||
GdeM/riNBFsiyxsBBAC54Pxg/8ZWaZX1phGdwfe5mek27SOYpC0AxIDCSOdMeQ6G
|
||||
HPk38pywl1d+S+KmF/F4Tdi+kWro62O4eG2uc/T8JQuRDUhSjX0Qa51gPzJrUOVT
|
||||
CFyUkiZ/3ZDhtXkgfuso8ua2ChBgR9Ngr4v43tSqa9y6AK7v0qjxD1x+xMrjXQAR
|
||||
AQABiQFxBBgBCgAmAhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsizTIFCQAN
|
||||
MRcAv7QgBBkBCgAdFiEEJcoVUVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62j
|
||||
UpRPICQq5gQApoWIigZxXFoM0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBS
|
||||
YnjyA4+n1D+zB2VqliD2QrsX12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZs
|
||||
nRJmXV+bsvD4sidLZLjdwOVa3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/
|
||||
U73GGi0D/i20VW8AWYAPACm2zMlzExKTOAV01YTQH/3vW0WLrOse53WcIVZga6es
|
||||
HuO4So0SOEAvxKMe5HpRIu2dJxTvd99Bo9xk9xJU0AoFrO0vNCRnL+5y68xMlODK
|
||||
lEw5/kl0jeaTBp6xX0HDQOEVOpPGUwWV4Ij2EnvfNDXaE1vK1kffiQFrBBgBCgAg
|
||||
AhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsi0AYAv7QgBBkBCgAdFiEEJcoV
|
||||
UVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62jUpRPICQq5gQApoWIigZxXFoM
|
||||
0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBSYnjyA4+n1D+zB2VqliD2QrsX
|
||||
12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZsnRJmXV+bsvD4sidLZLjdwOVa
|
||||
3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/U73GRl0EAJokkXmy4zKDHWWi
|
||||
wvK9gi2gQgRkVnu2AiONxJb5vjeLhM/07BRmH6K1o+w3fOeEQp4FjXj1eQ5fPSM6
|
||||
Hhwx2CTl9SDnPSBMiKXsEFRkmwQ2AAsQZLmQZvKBkLZYeBiwf+IY621eYDhZfo+G
|
||||
1dh1WoUCyREZsJQg2YoIpWIcvw+a
|
||||
=bNRo
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
`
|
||||
|
||||
const onlySubkeyNoPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
lQCVBFggvocBBAC7vBsHn7MKmS6IiiZNTXdciplVgS9cqVd+RTdIAoyNTcsiV1H0
|
||||
GQ3QtodOPeDlQDNoqinqaobd7R9g3m3hS53Nor7yBZkCWQ5x9v9JxRtoAq0sklh1
|
||||
I1X2zEqZk2l6YrfBF/64zWrhjnW3j23szkrAIVu0faQXbQ4z56tmZrw11wARAQAB
|
||||
/gdlAkdOVQG0CUdOVSBEdW1teYi4BBMBAgAiBQJYIL6HAhsDBgsJCAcDAgYVCAIJ
|
||||
CgsEFgIDAQIeAQIXgAAKCRCd1xxWp1CYAnjGA/9synn6ZXJUKAXQzySgmCZvCIbl
|
||||
rqBfEpxwLG4Q/lONhm5vthAE0z49I8hj5Gc5e2tLYUtq0o0OCRdCrYHa/efOYWpJ
|
||||
6RsK99bePOisVzmOABLIgZkcr022kHoMCmkPgv9CUGKP1yqbGl+zzAwQfUjRUmvD
|
||||
ZIcWLHi2ge4GzPMPi50B2ARYIL6cAQQAxWHnicKejAFcFcF1/3gUSgSH7eiwuBPX
|
||||
M7vDdgGzlve1o1jbV4tzrjN9jsCl6r0nJPDMfBSzgLr1auNTRG6HpJ4abcOx86ED
|
||||
Ad+avDcQPZb7z3dPhH/gb2lQejZsHh7bbeOS8WMSzHV3RqCLd8J/xwWPNR5zKn1f
|
||||
yp4IGfopidMAEQEAAQAD+wQOelnR82+dxyM2IFmZdOB9wSXQeCVOvxSaNMh6Y3lk
|
||||
UOOkO8Nlic4x0ungQRvjoRs4wBmCuwFK/MII6jKui0B7dn/NDf51i7rGdNGuJXDH
|
||||
e676By1sEY/NGkc74jr74T+5GWNU64W0vkpfgVmjSAzsUtpmhJMXsc7beBhJdnVl
|
||||
AgDKCb8hZqj1alcdmLoNvb7ibA3K/V8J462CPD7bMySPBa/uayoFhNxibpoXml2r
|
||||
oOtHa5izF3b0/9JY97F6rqkdAgD6GdTJ+xmlCoz1Sewoif1I6krq6xoa7gOYpIXo
|
||||
UL1Afr+LiJeyAnF/M34j/kjIVmPanZJjry0kkjHE5ILjH3uvAf4/6n9np+Th8ujS
|
||||
YDCIzKwR7639+H+qccOaddCep8Y6KGUMVdD/vTKEx1rMtK+hK/CDkkkxnFslifMJ
|
||||
kqoqv3WUqCWJAT0EGAECAAkFAlggvpwCGwIAqAkQndccVqdQmAKdIAQZAQIABgUC
|
||||
WCC+nAAKCRDmGUholQPwvQk+A/9latnSsR5s5/1A9TFki11GzSEnfLbx46FYOdkW
|
||||
n3YBxZoPQGxNA1vIn8GmouxZInw9CF4jdOJxEdzLlYQJ9YLTLtN5tQEMl/19/bR8
|
||||
/qLacAZ9IOezYRWxxZsyn6//jfl7A0Y+FV59d4YajKkEfItcIIlgVBSW6T+TNQT3
|
||||
R+EH5HJ/A/4/AN0CmBhhE2vGzTnVU0VPrE4V64pjn1rufFdclgpixNZCuuqpKpoE
|
||||
VVHn6mnBf4njKjZrAGPs5kfQ+H4NsM7v3Zz4yV6deu9FZc4O6E+V1WJ38rO8eBix
|
||||
7G2jko106CC6vtxsCPVIzY7aaG3H5pjRtomw+pX7SzrQ7FUg2PGumg==
|
||||
=F/T0
|
||||
-----END PGP PRIVATE KEY BLOCK-----`
|
||||
|
||||
const ecdsaPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xaUEX1KsSRMIKoZIzj0DAQcCAwTpYqJsnJiFhKKh+8TulWD+lVmerBFNS+Ii
|
||||
B+nlG3T0xQQ4Sy5eIjJ0CExIQQzi3EElF/Z2l4F3WC5taFA11NgA/gkDCHSS
|
||||
PThf1M2K4LN8F1MRcvR+sb7i0nH55ojkwuVB1DE6jqIT9m9i+mX1tzjSAS+6
|
||||
lPQiweCJvG7xTC7Hs3AzRapf/r1At4TB+v+5G2/CKynNFEJpbGwgPGJpbGxA
|
||||
aG9tZS5jb20+wncEEBMIAB8FAl9SrEkGCwkHCAMCBBUICgIDFgIBAhkBAhsD
|
||||
Ah4BAAoJEMpwT3+q3+xqw5UBAMebZN9isEZ1ML+R/jWAAWMwa/knMugrEZ1v
|
||||
Bl9+ZwM0AQCZdf80/wYY4Nve01qSRFv8OmKswLli3TvDv6FKc4cLz8epBF9S
|
||||
rEkSCCqGSM49AwEHAgMEAjKnT9b5wY2bf9TpAV3d7OUfPOxKj9c4VzeVzSrH
|
||||
AtQgo/MuI1cdYVURicV4i76DNjFhQHQFTk7BrC+C2u1yqQMBCAf+CQMIHImA
|
||||
iYfzQtjgQWSFZYUkCFpbbwhNF0ch+3HNaZkaHCnZRIsWsRnc6FCb6lRQyK9+
|
||||
Dq59kHlduE5QgY40894jfmP2JdJHU6nBdYrivbEdbMJhBBgTCAAJBQJfUqxJ
|
||||
AhsMAAoJEMpwT3+q3+xqUI0BAMykhV08kQ4Ip9Qlbss6Jdufv7YrU0Vd5hou
|
||||
b5TmiPd0APoDBh3qIic+aLLUcAuG3+Gt1P1AbUlmqV61ozn1WfHxfw==
|
||||
=KLN8
|
||||
-----END PGP PRIVATE KEY BLOCK-----`
|
||||
|
||||
const dsaPrivateKeyWithElGamalSubkey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
lQOBBF9/MLsRCACeaF6BI0jTgDAs86t8/kXPfwlPvR2MCYzB0BCqAdcq1hV/GTYd
|
||||
oNmJRna/ZJfsI/vf+d8Nv+EYOQkPheFS1MJVBitkAXjQPgm8i1tQWen1FCWZxqGk
|
||||
/vwZYF4yo8GhZ+Wxi3w09W9Cp9QM/CTmyE1Xe7wpPBGe+oD+me8Zxjyt8JBS4Qx+
|
||||
gvWbfHxfHnggh4pz7U8QkItlLsBNQEdX4R5+zwRN66g2ZSX/shaa/EkVnihUhD7r
|
||||
njP9I51ORWucTQD6OvgooaNQZCkQ/Se9TzdakwWKS2XSIFXiY/e2E5ZgKI/pfKDU
|
||||
iA/KessxddPb7nP/05OIJqg9AoDrD4vmehLzAQD+zsUS3LDU1m9/cG4LMsQbT2VK
|
||||
Te4HqbGIAle+eu/asQf8DDJMrbZpiJZvADum9j0TJ0oep6VdMbzo9RSDKvlLKT9m
|
||||
kG63H8oDWnCZm1a+HmGq9YIX+JHWmsLXXsFLeEouLzHO+mZo0X28eji3V2T87hyR
|
||||
MmUM0wFo4k7jK8uVmkDXv3XwNp2uByWxUKZd7EnWmcEZWqIiexJ7XpCS0Pg3tRaI
|
||||
zxve0SRe/dxfUPnTk/9KQ9hS6DWroBKquL182zx1Fggh4LIWWE2zq+UYn8BI0E8A
|
||||
rmIDFJdF8ymFQGRrEy6g79NnkPmkrZWsgMRYY65P6v4zLVmqohJKkpm3/Uxa6QAP
|
||||
CCoPh/JTOvPeCP2bOJH8z4Z9Py3ouMIjofQW8sXqRgf/RIHbh0KsINHrwwZ4gVIr
|
||||
MK3RofpaYxw1ztPIWb4cMWoWZHH1Pxh7ggTGSBpAhKXkiWw2Rxat8QF5aA7e962c
|
||||
bLvVv8dqsPrD/RnVJHag89cbPTzjn7gY9elE8EM8ithV3oQkwHTr4avYlpDZsgNd
|
||||
hUW3YgRwGo31tdzxoG04AcpV2t+07P8XMPr9hsfWs4rHohXPi38Hseu1Ji+dBoWQ
|
||||
3+1w/HH3o55s+jy4Ruaz78AIrjbmAJq+6rA2mIcCgrhw3DnzuwQAKeBvSeqn9zfS
|
||||
ZC812osMBVmkycwelpaIh64WZ0vWL3GvdXDctV2kXM+qVpDTLEny0LuiXxrwCKQL
|
||||
Ev4HAwK9uQBcreDEEud7pfRb8EYP5lzO2ZA7RaIvje6EWAGBvJGMRT0QQE5SGqc7
|
||||
Fw5geigBdt+vVyRuNNhg3c2fdn/OBQaYu0J/8AiOogG8EaM8tCFlbGdhbWFsQGRz
|
||||
YS5jb20gPGVsZ2FtYWxAZHNhLmNvbT6IkAQTEQgAOBYhBI+gnfiHQxB35/Dp0XAQ
|
||||
aE/rsWC5BQJffzC7AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEHAQaE/r
|
||||
sWC5A4EA/0GcJmyPtN+Klc7b9sVT3JgKTRnB/URxOJfYJofP0hZLAQCkqyMO+adV
|
||||
JvbgDH0zaITQWZSSXPqpgMpCA6juTrDsd50CawRffzC7EAgAxFFFSAAEQzWTgKU5
|
||||
EBtpxxoPzHqcChawTHRxHxjcELXzmUBS5PzfA1HXSPnNqK/x3Ut5ycC3CsW41Fnt
|
||||
Gm3706Wu9VFbFZVn55F9lPiplUo61n5pqMvOr1gmuQsdXiTa0t5FRa4TZ2VSiHFw
|
||||
vdAVSPTUsT4ZxJ1rPyFYRtq1n3pQcvdZowd07r0JnzTMjLLMFYCKhwIowoOC4zqJ
|
||||
iB8enjwOlpaqBATRm9xpVF7SJkroPF6/B1vdhj7E3c1aJyHlo0PYBAg756sSHWHg
|
||||
UuLyUQ4TA0hcCVenn/L/aSY2LnbdZB1EBhlYjA7dTCgwIqsQhfQmPkjz6g64A7+Y
|
||||
HbbrLwADBQgAk14QIEQ+J/VHetpQV/jt2pNsFK1kVK7mXK0spTExaC2yj2sXlHjL
|
||||
Ie3bO5T/KqmIaBEB5db5fA5xK9cZt79qrQHDKsEqUetUeMUWLBx77zBsus3grIgy
|
||||
bwDZKseRzQ715pwxquxQlScGoDIBKEh08HpwHkq140eIj3w+MAIfndaZaSCNaxaP
|
||||
Snky7BQmJ7Wc7qrIwoQP6yrnUqyW2yNi81nJYUhxjChqaFSlwzLs/iNGryBKo0ic
|
||||
BqVIRjikKHBlwBng6WyrltQo/Vt9GG8w+lqaAVXbJRlaBZJUR+2NKi/YhP3qQse3
|
||||
v8fi4kns0gh5LK+2C01RvdX4T49QSExuIf4HAwLJqYIGwadA2uem5v7/765ZtFWV
|
||||
oL0iZ0ueTJDby4wTFDpLVzzDi/uVcB0ZRFrGOp7w6OYcNYTtV8n3xmli2Q5Trw0c
|
||||
wZVzvg+ABKWiv7faBjMczIFF8y6WZKOIeAQYEQgAIBYhBI+gnfiHQxB35/Dp0XAQ
|
||||
aE/rsWC5BQJffzC7AhsMAAoJEHAQaE/rsWC5ZmIA/jhS4r4lClbvjuPWt0Yqdn7R
|
||||
fss2SPMYvMrrDh42aE0OAQD8xn4G6CN8UtW9xihXOY6FpxiJ/sMc2VaneeUd34oa
|
||||
4g==
|
||||
=XZm8
|
||||
-----END PGP PRIVATE KEY BLOCK-----`
|
||||
|
||||
// https://tests.sequoia-pgp.org/#Certificate_expiration
|
||||
// P _ U p
|
||||
const expiringPrimaryUIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
|
||||
/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz
|
||||
/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/
|
||||
5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3
|
||||
X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv
|
||||
9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0
|
||||
qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb
|
||||
SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb
|
||||
vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w
|
||||
bGU+wsEUBBMBCgBIBYJfiWKKBYkBX+/UBQsJCAcCCRD7/MgqAV5zMAYVCgkICwIE
|
||||
FgIDAQIXgAIbAwIeARYhBNGmbhojsYLJmA94jPv8yCoBXnMwAAAf0wv/a++a3DkL
|
||||
CttbK4LRIiSry2wb97mxYQoWYzvkKYD1IP/KiamRwzjBKuRSE0qZ2uonQDRGc+zl
|
||||
1H9dwkDLL4T9uJngCCPCBgFW/hOFPvF+WYEKHtOzunqx6KwDHkpdH+hpzfFzIhDo
|
||||
aXiVnDGvJ3H/bVTKq1m2KPXO2ckkXPQXJX9Fx6kPHdvcq+3ZI75IzbD/ue5lBcsy
|
||||
OKLzVu+KLxzlzGui6v1V0fTvU/uqvHlvUxcDAqMnIsDUPakjK2RDeQ38qtN6+PQ5
|
||||
/dT7vx2Wtzesqn2eDbDf5uRfSgmp2hJLJniAKjMCBVAJiOgPb0LXUIcwCGxaiWOA
|
||||
g5ZvNWjX5bZ/FxqpLpOE9OReI5YY7ns7zqP4thLYYWe0Qdp9a2ezVrgzgrh/SLla
|
||||
d73x/S9TrmLtYGGlbVUByJW+GXjW2Tt6iaa/WDFzx8NvZ/wzIAdGSEfLcvS+JBSP
|
||||
2ppdY5Ac/2dK3PzYABkHvB/rhXIwlXnrFDU9efRHZfFqQqGauA64wfdIzsDNBF2l
|
||||
nPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamX
|
||||
nn9sSXvIDEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMX
|
||||
SO4uImA+Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6
|
||||
rrd5y2AObaifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA
|
||||
0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/
|
||||
wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+pa
|
||||
LNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV
|
||||
8rUnR76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwz
|
||||
j8sxH48AEQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzy
|
||||
AhsMAAoJEPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+4
|
||||
1IL4rVcSKhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZ
|
||||
QanYmtSxcVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zp
|
||||
f3u0k14itcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn
|
||||
3OWjCPHVdTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK
|
||||
2b0vk/+wqMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFA
|
||||
ExaEK6VyjP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWi
|
||||
f9RSK4xjzRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj
|
||||
5KjhX2PVNEJd3XZRzaXZE2aAMQ==
|
||||
=522n
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
56
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go
generated
vendored
56
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.go
generated
vendored
|
@ -1,56 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// AEADConfig collects a number of AEAD parameters along with sensible defaults.
|
||||
// A nil AEADConfig is valid and results in all default values.
|
||||
type AEADConfig struct {
|
||||
// The AEAD mode of operation.
|
||||
DefaultMode AEADMode
|
||||
// Amount of octets in each chunk of data
|
||||
ChunkSize uint64
|
||||
}
|
||||
|
||||
// Mode returns the AEAD mode of operation.
|
||||
func (conf *AEADConfig) Mode() AEADMode {
|
||||
if conf == nil || conf.DefaultMode == 0 {
|
||||
return AEADModeEAX
|
||||
}
|
||||
mode := conf.DefaultMode
|
||||
if mode != AEADModeEAX && mode != AEADModeOCB &&
|
||||
mode != AEADModeExperimentalGCM {
|
||||
panic("AEAD mode unsupported")
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
// ChunkSizeByte returns the byte indicating the chunk size. The effective
|
||||
// chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6)
|
||||
func (conf *AEADConfig) ChunkSizeByte() byte {
|
||||
if conf == nil || conf.ChunkSize == 0 {
|
||||
return 12 // 1 << (12 + 6) == 262144 bytes
|
||||
}
|
||||
|
||||
chunkSize := conf.ChunkSize
|
||||
exponent := bits.Len64(chunkSize) - 1
|
||||
switch {
|
||||
case exponent < 6:
|
||||
exponent = 6
|
||||
case exponent > 27:
|
||||
exponent = 27
|
||||
}
|
||||
|
||||
return byte(exponent - 6)
|
||||
}
|
||||
|
||||
// decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the
|
||||
// maximum returned value is 1 << 30.
|
||||
func decodeAEADChunkSize(c byte) int {
|
||||
size := uint64(1 << (c + 6))
|
||||
if size != uint64(int(size)) {
|
||||
return 1 << 30
|
||||
}
|
||||
return int(size)
|
||||
}
|
364
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go
generated
vendored
364
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.go
generated
vendored
|
@ -1,364 +0,0 @@
|
|||
// Copyright (C) 2019 ProtonTech AG
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// AEADEncrypted represents an AEAD Encrypted Packet (tag 20, RFC4880bis-5.16).
|
||||
type AEADEncrypted struct {
|
||||
cipher CipherFunction
|
||||
mode AEADMode
|
||||
chunkSizeByte byte
|
||||
Contents io.Reader // Encrypted chunks and tags
|
||||
initialNonce []byte // Referred to as IV in RFC4880-bis
|
||||
}
|
||||
|
||||
// Only currently defined version
|
||||
const aeadEncryptedVersion = 1
|
||||
|
||||
// An AEAD opener/sealer, its configuration, and data for en/decryption.
|
||||
type aeadCrypter struct {
|
||||
aead cipher.AEAD
|
||||
chunkSize int
|
||||
initialNonce []byte
|
||||
associatedData []byte // Chunk-independent associated data
|
||||
chunkIndex []byte // Chunk counter
|
||||
bytesProcessed int // Amount of plaintext bytes encrypted/decrypted
|
||||
buffer bytes.Buffer // Buffered bytes accross chunks
|
||||
}
|
||||
|
||||
// aeadEncrypter encrypts and writes bytes. It encrypts when necessary according
|
||||
// to the AEAD block size, and buffers the extra encrypted bytes for next write.
|
||||
type aeadEncrypter struct {
|
||||
aeadCrypter // Embedded plaintext sealer
|
||||
writer io.WriteCloser // 'writer' is a partialLengthWriter
|
||||
}
|
||||
|
||||
// aeadDecrypter reads and decrypts bytes. It buffers extra decrypted bytes when
|
||||
// necessary, similar to aeadEncrypter.
|
||||
type aeadDecrypter struct {
|
||||
aeadCrypter // Embedded ciphertext opener
|
||||
reader io.Reader // 'reader' is a partialLengthReader
|
||||
peekedBytes []byte // Used to detect last chunk
|
||||
eof bool
|
||||
}
|
||||
|
||||
func (ae *AEADEncrypted) parse(buf io.Reader) error {
|
||||
headerData := make([]byte, 4)
|
||||
if n, err := io.ReadFull(buf, headerData); n < 4 {
|
||||
return errors.AEADError("could not read aead header:" + err.Error())
|
||||
}
|
||||
// Read initial nonce
|
||||
mode := AEADMode(headerData[2])
|
||||
nonceLen := mode.NonceLength()
|
||||
if nonceLen == 0 {
|
||||
return errors.AEADError("unknown mode")
|
||||
}
|
||||
initialNonce := make([]byte, nonceLen)
|
||||
if n, err := io.ReadFull(buf, initialNonce); n < nonceLen {
|
||||
return errors.AEADError("could not read aead nonce:" + err.Error())
|
||||
}
|
||||
ae.Contents = buf
|
||||
ae.initialNonce = initialNonce
|
||||
c := headerData[1]
|
||||
if _, ok := algorithm.CipherById[c]; !ok {
|
||||
return errors.UnsupportedError("unknown cipher: " + string(c))
|
||||
}
|
||||
ae.cipher = CipherFunction(c)
|
||||
ae.mode = mode
|
||||
ae.chunkSizeByte = byte(headerData[3])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or
|
||||
// an error.
|
||||
func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) {
|
||||
return ae.decrypt(key)
|
||||
}
|
||||
|
||||
// decrypt prepares an aeadCrypter and returns a ReadCloser from which
|
||||
// decrypted bytes can be read (see aeadDecrypter.Read()).
|
||||
func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) {
|
||||
blockCipher := ae.cipher.new(key)
|
||||
aead := ae.mode.new(blockCipher)
|
||||
// Carry the first tagLen bytes
|
||||
tagLen := ae.mode.TagLength()
|
||||
peekedBytes := make([]byte, tagLen)
|
||||
n, err := io.ReadFull(ae.Contents, peekedBytes)
|
||||
if n < tagLen || (err != nil && err != io.EOF) {
|
||||
return nil, errors.AEADError("Not enough data to decrypt:" + err.Error())
|
||||
}
|
||||
chunkSize := decodeAEADChunkSize(ae.chunkSizeByte)
|
||||
return &aeadDecrypter{
|
||||
aeadCrypter: aeadCrypter{
|
||||
aead: aead,
|
||||
chunkSize: chunkSize,
|
||||
initialNonce: ae.initialNonce,
|
||||
associatedData: ae.associatedData(),
|
||||
chunkIndex: make([]byte, 8),
|
||||
},
|
||||
reader: ae.Contents,
|
||||
peekedBytes: peekedBytes}, nil
|
||||
}
|
||||
|
||||
// Read decrypts bytes and reads them into dst. It decrypts when necessary and
|
||||
// buffers extra decrypted bytes. It returns the number of bytes copied into dst
|
||||
// and an error.
|
||||
func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
|
||||
// Return buffered plaintext bytes from previous calls
|
||||
if ar.buffer.Len() > 0 {
|
||||
return ar.buffer.Read(dst)
|
||||
}
|
||||
|
||||
// Return EOF if we've previously validated the final tag
|
||||
if ar.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Read a chunk
|
||||
tagLen := ar.aead.Overhead()
|
||||
cipherChunkBuf := new(bytes.Buffer)
|
||||
_, errRead := io.CopyN(cipherChunkBuf, ar.reader, int64(ar.chunkSize + tagLen))
|
||||
cipherChunk := cipherChunkBuf.Bytes()
|
||||
if errRead != nil && errRead != io.EOF {
|
||||
return 0, errRead
|
||||
}
|
||||
decrypted, errChunk := ar.openChunk(cipherChunk)
|
||||
if errChunk != nil {
|
||||
return 0, errChunk
|
||||
}
|
||||
|
||||
// Return decrypted bytes, buffering if necessary
|
||||
if len(dst) < len(decrypted) {
|
||||
n = copy(dst, decrypted[:len(dst)])
|
||||
ar.buffer.Write(decrypted[len(dst):])
|
||||
} else {
|
||||
n = copy(dst, decrypted)
|
||||
}
|
||||
|
||||
// Check final authentication tag
|
||||
if errRead == io.EOF {
|
||||
errChunk := ar.validateFinalTag(ar.peekedBytes)
|
||||
if errChunk != nil {
|
||||
return n, errChunk
|
||||
}
|
||||
ar.eof = true // Mark EOF for when we've returned all buffered data
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close is noOp. The final authentication tag of the stream was already
|
||||
// checked in the last Read call. In the future, this function could be used to
|
||||
// wipe the reader and peeked, decrypted bytes, if necessary.
|
||||
func (ar *aeadDecrypter) Close() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeAEADEncrypted initializes the aeadCrypter and returns a writer.
|
||||
// This writer encrypts and writes bytes (see aeadEncrypter.Write()).
|
||||
func SerializeAEADEncrypted(w io.Writer, key []byte, cipher CipherFunction, mode AEADMode, config *Config) (io.WriteCloser, error) {
|
||||
writeCloser := noOpCloser{w}
|
||||
writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Data for en/decryption: tag, version, cipher, aead mode, chunk size
|
||||
aeadConf := config.AEAD()
|
||||
prefix := []byte{
|
||||
0xD4,
|
||||
aeadEncryptedVersion,
|
||||
byte(config.Cipher()),
|
||||
byte(aeadConf.Mode()),
|
||||
aeadConf.ChunkSizeByte(),
|
||||
}
|
||||
n, err := writer.Write(prefix[1:])
|
||||
if err != nil || n < 4 {
|
||||
return nil, errors.AEADError("could not write AEAD headers")
|
||||
}
|
||||
// Sample nonce
|
||||
nonceLen := aeadConf.Mode().NonceLength()
|
||||
nonce := make([]byte, nonceLen)
|
||||
n, err = rand.Read(nonce)
|
||||
if err != nil {
|
||||
panic("Could not sample random nonce")
|
||||
}
|
||||
_, err = writer.Write(nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockCipher := CipherFunction(config.Cipher()).new(key)
|
||||
alg := AEADMode(aeadConf.Mode()).new(blockCipher)
|
||||
|
||||
chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte())
|
||||
return &aeadEncrypter{
|
||||
aeadCrypter: aeadCrypter{
|
||||
aead: alg,
|
||||
chunkSize: chunkSize,
|
||||
associatedData: prefix,
|
||||
chunkIndex: make([]byte, 8),
|
||||
initialNonce: nonce,
|
||||
},
|
||||
writer: writer}, nil
|
||||
}
|
||||
|
||||
// Write encrypts and writes bytes. It encrypts when necessary and buffers extra
|
||||
// plaintext bytes for next call. When the stream is finished, Close() MUST be
|
||||
// called to append the final tag.
|
||||
func (aw *aeadEncrypter) Write(plaintextBytes []byte) (n int, err error) {
|
||||
// Append plaintextBytes to existing buffered bytes
|
||||
n, err = aw.buffer.Write(plaintextBytes)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
// Encrypt and write chunks
|
||||
for aw.buffer.Len() >= aw.chunkSize {
|
||||
plainChunk := aw.buffer.Next(aw.chunkSize)
|
||||
encryptedChunk, err := aw.sealChunk(plainChunk)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
_, err = aw.writer.Write(encryptedChunk)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close encrypts and writes the remaining buffered plaintext if any, appends
|
||||
// the final authentication tag, and closes the embedded writer. This function
|
||||
// MUST be called at the end of a stream.
|
||||
func (aw *aeadEncrypter) Close() (err error) {
|
||||
// Encrypt and write a chunk if there's buffered data left, or if we haven't
|
||||
// written any chunks yet.
|
||||
if aw.buffer.Len() > 0 || aw.bytesProcessed == 0 {
|
||||
plainChunk := aw.buffer.Bytes()
|
||||
lastEncryptedChunk, err := aw.sealChunk(plainChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = aw.writer.Write(lastEncryptedChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Compute final tag (associated data: packet tag, version, cipher, aead,
|
||||
// chunk size, index, total number of encrypted octets).
|
||||
adata := append(aw.associatedData[:], aw.chunkIndex[:]...)
|
||||
adata = append(adata, make([]byte, 8)...)
|
||||
binary.BigEndian.PutUint64(adata[13:], uint64(aw.bytesProcessed))
|
||||
nonce := aw.computeNextNonce()
|
||||
finalTag := aw.aead.Seal(nil, nonce, nil, adata)
|
||||
_, err = aw.writer.Write(finalTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return aw.writer.Close()
|
||||
}
|
||||
|
||||
// sealChunk Encrypts and authenticates the given chunk.
|
||||
func (aw *aeadEncrypter) sealChunk(data []byte) ([]byte, error) {
|
||||
if len(data) > aw.chunkSize {
|
||||
return nil, errors.AEADError("chunk exceeds maximum length")
|
||||
}
|
||||
if aw.associatedData == nil {
|
||||
return nil, errors.AEADError("can't seal without headers")
|
||||
}
|
||||
adata := append(aw.associatedData, aw.chunkIndex...)
|
||||
nonce := aw.computeNextNonce()
|
||||
encrypted := aw.aead.Seal(nil, nonce, data, adata)
|
||||
aw.bytesProcessed += len(data)
|
||||
if err := aw.aeadCrypter.incrementIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
// openChunk decrypts and checks integrity of an encrypted chunk, returning
|
||||
// the underlying plaintext and an error. It access peeked bytes from next
|
||||
// chunk, to identify the last chunk and decrypt/validate accordingly.
|
||||
func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
|
||||
tagLen := ar.aead.Overhead()
|
||||
// Restore carried bytes from last call
|
||||
chunkExtra := append(ar.peekedBytes, data...)
|
||||
// 'chunk' contains encrypted bytes, followed by an authentication tag.
|
||||
chunk := chunkExtra[:len(chunkExtra)-tagLen]
|
||||
ar.peekedBytes = chunkExtra[len(chunkExtra)-tagLen:]
|
||||
adata := append(ar.associatedData, ar.chunkIndex...)
|
||||
nonce := ar.computeNextNonce()
|
||||
plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ar.bytesProcessed += len(plainChunk)
|
||||
if err = ar.aeadCrypter.incrementIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainChunk, nil
|
||||
}
|
||||
|
||||
// Checks the summary tag. It takes into account the total decrypted bytes into
|
||||
// the associated data. It returns an error, or nil if the tag is valid.
|
||||
func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
|
||||
// Associated: tag, version, cipher, aead, chunk size, index, and octets
|
||||
amountBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed))
|
||||
adata := append(ar.associatedData, ar.chunkIndex...)
|
||||
adata = append(adata, amountBytes...)
|
||||
nonce := ar.computeNextNonce()
|
||||
_, err := ar.aead.Open(nil, nonce, tag, adata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Associated data for chunks: tag, version, cipher, mode, chunk size byte
|
||||
func (ae *AEADEncrypted) associatedData() []byte {
|
||||
return []byte{
|
||||
0xD4,
|
||||
aeadEncryptedVersion,
|
||||
byte(ae.cipher),
|
||||
byte(ae.mode),
|
||||
ae.chunkSizeByte}
|
||||
}
|
||||
|
||||
// computeNonce takes the incremental index and computes an eXclusive OR with
|
||||
// the least significant 8 bytes of the receivers' initial nonce (see sec.
|
||||
// 5.16.1 and 5.16.2). It returns the resulting nonce.
|
||||
func (wo *aeadCrypter) computeNextNonce() (nonce []byte) {
|
||||
nonce = make([]byte, len(wo.initialNonce))
|
||||
copy(nonce, wo.initialNonce)
|
||||
offset := len(wo.initialNonce) - 8
|
||||
for i := 0; i < 8; i++ {
|
||||
nonce[i+offset] ^= wo.chunkIndex[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// incrementIndex perfoms an integer increment by 1 of the integer represented by the
|
||||
// slice, modifying it accordingly.
|
||||
func (wo *aeadCrypter) incrementIndex() error {
|
||||
index := wo.chunkIndex
|
||||
if len(index) == 0 {
|
||||
return errors.AEADError("Index has length 0")
|
||||
}
|
||||
for i := len(index) - 1; i >= 0; i-- {
|
||||
if index[i] < 255 {
|
||||
index[i]++
|
||||
return nil
|
||||
}
|
||||
index[i] = 0
|
||||
}
|
||||
return errors.AEADError("cannot further increment index")
|
||||
}
|
123
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
123
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
|
@ -1,123 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Compressed represents a compressed OpenPGP packet. The decompressed contents
|
||||
// will contain more OpenPGP packets. See RFC 4880, section 5.6.
|
||||
type Compressed struct {
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
const (
|
||||
NoCompression = flate.NoCompression
|
||||
BestSpeed = flate.BestSpeed
|
||||
BestCompression = flate.BestCompression
|
||||
DefaultCompression = flate.DefaultCompression
|
||||
)
|
||||
|
||||
// CompressionConfig contains compressor configuration settings.
|
||||
type CompressionConfig struct {
|
||||
// Level is the compression level to use. It must be set to
|
||||
// between -1 and 9, with -1 causing the compressor to use the
|
||||
// default compression level, 0 causing the compressor to use
|
||||
// no compression and 1 to 9 representing increasing (better,
|
||||
// slower) compression levels. If Level is less than -1 or
|
||||
// more then 9, a non-nil error will be returned during
|
||||
// encryption. See the constants above for convenient common
|
||||
// settings for Level.
|
||||
Level int
|
||||
}
|
||||
|
||||
func (c *Compressed) parse(r io.Reader) error {
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case 1:
|
||||
c.Body = flate.NewReader(r)
|
||||
case 2:
|
||||
c.Body, err = zlib.NewReader(r)
|
||||
case 3:
|
||||
c.Body = bzip2.NewReader(r)
|
||||
default:
|
||||
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// compressedWriterCloser represents the serialized compression stream
|
||||
// header and the compressor. Its Close() method ensures that both the
|
||||
// compressor and serialized stream header are closed. Its Write()
|
||||
// method writes to the compressor.
|
||||
type compressedWriteCloser struct {
|
||||
sh io.Closer // Stream Header
|
||||
c io.WriteCloser // Compressor
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Write(p []byte) (int, error) {
|
||||
return cwc.c.Write(p)
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Close() (err error) {
|
||||
err = cwc.c.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cwc.sh.Close()
|
||||
}
|
||||
|
||||
// SerializeCompressed serializes a compressed data packet to w and
|
||||
// returns a WriteCloser to which the literal data packets themselves
|
||||
// can be written and which MUST be closed on completion. If cc is
|
||||
// nil, sensible defaults will be used to configure the compression
|
||||
// algorithm.
|
||||
func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) {
|
||||
compressed, err := serializeStreamHeader(w, packetTypeCompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = compressed.Write([]byte{uint8(algo)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
level := DefaultCompression
|
||||
if cc != nil {
|
||||
level = cc.Level
|
||||
}
|
||||
|
||||
var compressor io.WriteCloser
|
||||
switch algo {
|
||||
case CompressionZIP:
|
||||
compressor, err = flate.NewWriter(compressed, level)
|
||||
case CompressionZLIB:
|
||||
compressor, err = zlib.NewWriterLevel(compressed, level)
|
||||
default:
|
||||
s := strconv.Itoa(int(algo))
|
||||
err = errors.UnsupportedError("Unsupported compression algorithm: " + s)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
literaldata = compressedWriteCloser{compressed, compressor}
|
||||
|
||||
return
|
||||
}
|
167
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
167
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
|
@ -1,167 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config collects a number of parameters along with sensible defaults.
|
||||
// A nil *Config is valid and results in all default values.
|
||||
type Config struct {
|
||||
// Rand provides the source of entropy.
|
||||
// If nil, the crypto/rand Reader is used.
|
||||
Rand io.Reader
|
||||
// DefaultHash is the default hash function to be used.
|
||||
// If zero, SHA-256 is used.
|
||||
DefaultHash crypto.Hash
|
||||
// DefaultCipher is the cipher to be used.
|
||||
// If zero, AES-128 is used.
|
||||
DefaultCipher CipherFunction
|
||||
// Time returns the current time as the number of seconds since the
|
||||
// epoch. If Time is nil, time.Now is used.
|
||||
Time func() time.Time
|
||||
// DefaultCompressionAlgo is the compression algorithm to be
|
||||
// applied to the plaintext before encryption. If zero, no
|
||||
// compression is done.
|
||||
DefaultCompressionAlgo CompressionAlgo
|
||||
// CompressionConfig configures the compression settings.
|
||||
CompressionConfig *CompressionConfig
|
||||
// S2KCount is only used for symmetric encryption. It
|
||||
// determines the strength of the passphrase stretching when
|
||||
// the said passphrase is hashed to produce a key. S2KCount
|
||||
// should be between 1024 and 65011712, inclusive. If Config
|
||||
// is nil or S2KCount is 0, the value 65536 used. Not all
|
||||
// values in the above range can be represented. S2KCount will
|
||||
// be rounded up to the next representable value if it cannot
|
||||
// be encoded exactly. When set, it is strongly encrouraged to
|
||||
// use a value that is at least 65536. See RFC 4880 Section
|
||||
// 3.7.1.3.
|
||||
S2KCount int
|
||||
// RSABits is the number of bits in new RSA keys made with NewEntity.
|
||||
// If zero, then 2048 bit keys are created.
|
||||
RSABits int
|
||||
// The public key algorithm to use - will always create a signing primary
|
||||
// key and encryption subkey.
|
||||
Algorithm PublicKeyAlgorithm
|
||||
// Some known primes that are optionally prepopulated by the caller
|
||||
RSAPrimes []*big.Int
|
||||
// AEADConfig configures the use of the new AEAD Encrypted Data Packet,
|
||||
// defined in the draft of the next version of the OpenPGP specification.
|
||||
// If a non-nil AEADConfig is passed, usage of this packet is enabled. By
|
||||
// default, it is disabled. See the documentation of AEADConfig for more
|
||||
// configuration options related to AEAD.
|
||||
// **Note: using this option may break compatibility with other OpenPGP
|
||||
// implementations, as well as future versions of this library.**
|
||||
AEADConfig *AEADConfig
|
||||
// V5Keys configures version 5 key generation. If false, this package still
|
||||
// supports version 5 keys, but produces version 4 keys.
|
||||
V5Keys bool
|
||||
// "The validity period of the key. This is the number of seconds after
|
||||
// the key creation time that the key expires. If this is not present
|
||||
// or has a value of zero, the key never expires. This is found only on
|
||||
// a self-signature.""
|
||||
// https://tools.ietf.org/html/rfc4880#section-5.2.3.6
|
||||
KeyLifetimeSecs uint32
|
||||
// "The validity period of the signature. This is the number of seconds
|
||||
// after the signature creation time that the signature expires. If
|
||||
// this is not present or has a value of zero, it never expires."
|
||||
// https://tools.ietf.org/html/rfc4880#section-5.2.3.10
|
||||
SigLifetimeSecs uint32
|
||||
// SigningKeyId is used to specify the signing key to use (by Key ID).
|
||||
// By default, the signing key is selected automatically, preferring
|
||||
// signing subkeys if available.
|
||||
SigningKeyId uint64
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
if c == nil || c.Rand == nil {
|
||||
return rand.Reader
|
||||
}
|
||||
return c.Rand
|
||||
}
|
||||
|
||||
func (c *Config) Hash() crypto.Hash {
|
||||
if c == nil || uint(c.DefaultHash) == 0 {
|
||||
return crypto.SHA256
|
||||
}
|
||||
return c.DefaultHash
|
||||
}
|
||||
|
||||
func (c *Config) Cipher() CipherFunction {
|
||||
if c == nil || uint8(c.DefaultCipher) == 0 {
|
||||
return CipherAES128
|
||||
}
|
||||
return c.DefaultCipher
|
||||
}
|
||||
|
||||
func (c *Config) Now() time.Time {
|
||||
if c == nil || c.Time == nil {
|
||||
return time.Now()
|
||||
}
|
||||
return c.Time()
|
||||
}
|
||||
|
||||
// KeyLifetime returns the validity period of the key.
|
||||
func (c *Config) KeyLifetime() uint32 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.KeyLifetimeSecs
|
||||
}
|
||||
|
||||
// SigLifetime returns the validity period of the signature.
|
||||
func (c *Config) SigLifetime() uint32 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.SigLifetimeSecs
|
||||
}
|
||||
|
||||
func (c *Config) Compression() CompressionAlgo {
|
||||
if c == nil {
|
||||
return CompressionNone
|
||||
}
|
||||
return c.DefaultCompressionAlgo
|
||||
}
|
||||
|
||||
func (c *Config) PasswordHashIterations() int {
|
||||
if c == nil || c.S2KCount == 0 {
|
||||
return 0
|
||||
}
|
||||
return c.S2KCount
|
||||
}
|
||||
|
||||
func (c *Config) RSAModulusBits() int {
|
||||
if c == nil || c.RSABits == 0 {
|
||||
return 2048
|
||||
}
|
||||
return c.RSABits
|
||||
}
|
||||
|
||||
func (c *Config) PublicKeyAlgorithm() PublicKeyAlgorithm {
|
||||
if c == nil || c.Algorithm == 0 {
|
||||
return PubKeyAlgoRSA
|
||||
}
|
||||
return c.Algorithm
|
||||
}
|
||||
|
||||
func (c *Config) AEAD() *AEADConfig {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.AEADConfig
|
||||
}
|
||||
|
||||
func (c *Config) SigningKey() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.SigningKeyId
|
||||
}
|
282
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
282
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
|
@ -1,282 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
)
|
||||
|
||||
const encryptedKeyVersion = 3
|
||||
|
||||
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
|
||||
// section 5.1.
|
||||
type EncryptedKey struct {
|
||||
KeyId uint64
|
||||
Algo PublicKeyAlgorithm
|
||||
CipherFunc CipherFunction // only valid after a successful Decrypt
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
|
||||
encryptedMPI1, encryptedMPI2 encoding.Field
|
||||
}
|
||||
|
||||
func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
var buf [10]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != encryptedKeyVersion {
|
||||
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
e.KeyId = binary.BigEndian.Uint64(buf[1:9])
|
||||
e.Algo = PublicKeyAlgorithm(buf[9])
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoElGamal:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.encryptedMPI2 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoECDH:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.encryptedMPI2 = new(encoding.OID)
|
||||
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = consumeAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 {
|
||||
var checksum uint16
|
||||
for _, v := range key {
|
||||
checksum += uint16(v)
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted session key with the given private key. The
|
||||
// private key must have been decrypted first.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
if e.KeyId != 0 && e.KeyId != priv.KeyId {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16))
|
||||
}
|
||||
if e.Algo != priv.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
if priv.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
|
||||
var err error
|
||||
var b []byte
|
||||
|
||||
// TODO(agl): use session key decryption routines here to avoid
|
||||
// padding oracle attacks.
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
// Supports both *rsa.PrivateKey and crypto.Decrypter
|
||||
k := priv.PrivateKey.(crypto.Decrypter)
|
||||
b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.Bytes()), nil)
|
||||
case PubKeyAlgoElGamal:
|
||||
c1 := new(big.Int).SetBytes(e.encryptedMPI1.Bytes())
|
||||
c2 := new(big.Int).SetBytes(e.encryptedMPI2.Bytes())
|
||||
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
|
||||
case PubKeyAlgoECDH:
|
||||
vsG := e.encryptedMPI1.Bytes()
|
||||
m := e.encryptedMPI2.Bytes()
|
||||
oid := priv.PublicKey.oid.EncodedBytes()
|
||||
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:])
|
||||
default:
|
||||
err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.CipherFunc = CipherFunction(b[0])
|
||||
e.Key = b[1 : len(b)-2]
|
||||
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
|
||||
checksum := checksumKeyMaterial(e.Key)
|
||||
if checksum != expectedChecksum {
|
||||
return errors.StructuralError("EncryptedKey checksum incorrect")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the encrypted key packet, e, to w.
|
||||
func (e *EncryptedKey) Serialize(w io.Writer) error {
|
||||
var mpiLen int
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength())
|
||||
case PubKeyAlgoElGamal:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
case PubKeyAlgoECDH:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
default:
|
||||
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
|
||||
}
|
||||
|
||||
err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Write([]byte{encryptedKeyVersion})
|
||||
binary.Write(w, binary.BigEndian, e.KeyId)
|
||||
w.Write([]byte{byte(e.Algo)})
|
||||
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
_, err := w.Write(e.encryptedMPI1.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoElGamal:
|
||||
if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoECDH:
|
||||
if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
|
||||
return err
|
||||
default:
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
|
||||
var buf [10]byte
|
||||
buf[0] = encryptedKeyVersion
|
||||
binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
|
||||
buf[9] = byte(pub.PubKeyAlgo)
|
||||
|
||||
keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */)
|
||||
keyBlock[0] = byte(cipherFunc)
|
||||
copy(keyBlock[1:], key)
|
||||
checksum := checksumKeyMaterial(key)
|
||||
keyBlock[1+len(key)] = byte(checksum >> 8)
|
||||
keyBlock[1+len(key)+1] = byte(checksum)
|
||||
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
|
||||
case PubKeyAlgoElGamal:
|
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
|
||||
case PubKeyAlgoECDH:
|
||||
return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
|
||||
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
|
||||
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error {
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
cipherMPI := encoding.NewMPI(cipherText)
|
||||
packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(cipherMPI.EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error {
|
||||
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := 10 /* header length */
|
||||
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
|
||||
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(new(encoding.MPI).SetBig(c1).EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(new(encoding.MPI).SetBig(c2).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
|
||||
vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
g := encoding.NewMPI(vsG)
|
||||
m := encoding.NewOID(c)
|
||||
|
||||
packetLen := 10 /* header length */
|
||||
packetLen += int(g.EncodedLength()) + int(m.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(g.EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(m.EncodedBytes())
|
||||
return err
|
||||
}
|
91
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
91
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// LiteralData represents an encrypted file. See RFC 4880, section 5.9.
|
||||
type LiteralData struct {
|
||||
Format uint8
|
||||
IsBinary bool
|
||||
FileName string
|
||||
Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined.
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
// ForEyesOnly returns whether the contents of the LiteralData have been marked
|
||||
// as especially sensitive.
|
||||
func (l *LiteralData) ForEyesOnly() bool {
|
||||
return l.FileName == "_CONSOLE"
|
||||
}
|
||||
|
||||
func (l *LiteralData) parse(r io.Reader) (err error) {
|
||||
var buf [256]byte
|
||||
|
||||
_, err = readFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Format = buf[0]
|
||||
l.IsBinary = l.Format == 'b'
|
||||
fileNameLen := int(buf[1])
|
||||
|
||||
_, err = readFull(r, buf[:fileNameLen])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.FileName = string(buf[:fileNameLen])
|
||||
|
||||
_, err = readFull(r, buf[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Time = binary.BigEndian.Uint32(buf[:4])
|
||||
l.Body = r
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeLiteral serializes a literal data packet to w and returns a
|
||||
// WriteCloser to which the data itself can be written and which MUST be closed
|
||||
// on completion. The fileName is truncated to 255 bytes.
|
||||
func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) {
|
||||
var buf [4]byte
|
||||
buf[0] = 't'
|
||||
if isBinary {
|
||||
buf[0] = 'b'
|
||||
}
|
||||
if len(fileName) > 255 {
|
||||
fileName = fileName[:255]
|
||||
}
|
||||
buf[1] = byte(len(fileName))
|
||||
|
||||
inner, err := serializeStreamHeader(w, packetTypeLiteralData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = inner.Write(buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = inner.Write([]byte(fileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[:], time)
|
||||
_, err = inner.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
plaintext = inner
|
||||
return
|
||||
}
|
137
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go
generated
vendored
137
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.go
generated
vendored
|
@ -1,137 +0,0 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type ocfbEncrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// An OCFBResyncOption determines if the "resynchronization step" of OCFB is
|
||||
// performed.
|
||||
type OCFBResyncOption bool
|
||||
|
||||
const (
|
||||
OCFBResync OCFBResyncOption = true
|
||||
OCFBNoResync OCFBResyncOption = false
|
||||
)
|
||||
|
||||
// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block, and an initial amount of
|
||||
// ciphertext. randData must be random bytes and be the same length as the
|
||||
// cipher.Block's block size. Resync determines if the "resynchronization step"
|
||||
// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on
|
||||
// this point.
|
||||
func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) {
|
||||
blockSize := block.BlockSize()
|
||||
if len(randData) != blockSize {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
x := &ocfbEncrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefix := make([]byte, blockSize+2)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefix[i] = randData[i] ^ x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefix[blockSize] = x.fre[0] ^ randData[blockSize-2]
|
||||
prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1]
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
return x, prefix
|
||||
}
|
||||
|
||||
func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
x.fre[x.outUsed] ^= src[i]
|
||||
dst[i] = x.fre[x.outUsed]
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
||||
|
||||
type ocfbDecrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block. Prefix must be the first
|
||||
// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's
|
||||
// block size. On successful exit, blockSize+2 bytes of decrypted data are written into
|
||||
// prefix. Resync determines if the "resynchronization step" from RFC 4880,
|
||||
// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point.
|
||||
func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream {
|
||||
blockSize := block.BlockSize()
|
||||
if len(prefix) != blockSize+2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
x := &ocfbDecrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefixCopy := make([]byte, len(prefix))
|
||||
copy(prefixCopy, prefix)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefixCopy[i] ^= x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefixCopy[blockSize] ^= x.fre[0]
|
||||
prefixCopy[blockSize+1] ^= x.fre[1]
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
copy(prefix, prefixCopy)
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
c := src[i]
|
||||
dst[i] = x.fre[x.outUsed] ^ src[i]
|
||||
x.fre[x.outUsed] = c
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
73
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
73
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
||||
// section 5.4.
|
||||
type OnePassSignature struct {
|
||||
SigType SignatureType
|
||||
Hash crypto.Hash
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
KeyId uint64
|
||||
IsLast bool
|
||||
}
|
||||
|
||||
const onePassSignatureVersion = 3
|
||||
|
||||
func (ops *OnePassSignature) parse(r io.Reader) (err error) {
|
||||
var buf [13]byte
|
||||
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != onePassSignatureVersion {
|
||||
err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
var ok bool
|
||||
ops.Hash, ok = s2k.HashIdToHash(buf[2])
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2])))
|
||||
}
|
||||
|
||||
ops.SigType = SignatureType(buf[1])
|
||||
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3])
|
||||
ops.KeyId = binary.BigEndian.Uint64(buf[4:12])
|
||||
ops.IsLast = buf[12] != 0
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the given OnePassSignature to w.
|
||||
func (ops *OnePassSignature) Serialize(w io.Writer) error {
|
||||
var buf [13]byte
|
||||
buf[0] = onePassSignatureVersion
|
||||
buf[1] = uint8(ops.SigType)
|
||||
var ok bool
|
||||
buf[2], ok = s2k.HashToHashId(ops.Hash)
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
|
||||
}
|
||||
buf[3] = uint8(ops.PubKeyAlgo)
|
||||
binary.BigEndian.PutUint64(buf[4:12], ops.KeyId)
|
||||
if ops.IsLast {
|
||||
buf[12] = 1
|
||||
}
|
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(buf[:])
|
||||
return err
|
||||
}
|
162
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
162
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
|
@ -1,162 +0,0 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is
|
||||
// useful for splitting and storing the original packet contents separately,
|
||||
// handling unsupported packet types or accessing parts of the packet not yet
|
||||
// implemented by this package.
|
||||
type OpaquePacket struct {
|
||||
// Packet type
|
||||
Tag uint8
|
||||
// Reason why the packet was parsed opaquely
|
||||
Reason error
|
||||
// Binary contents of the packet data
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
func (op *OpaquePacket) parse(r io.Reader) (err error) {
|
||||
op.Contents, err = ioutil.ReadAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the packet to a writer in its original form, including
|
||||
// the packet header.
|
||||
func (op *OpaquePacket) Serialize(w io.Writer) (err error) {
|
||||
err = serializeHeader(w, packetType(op.Tag), len(op.Contents))
|
||||
if err == nil {
|
||||
_, err = w.Write(op.Contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Parse attempts to parse the opaque contents into a structure supported by
|
||||
// this package. If the packet is not known then the result will be another
|
||||
// OpaquePacket.
|
||||
func (op *OpaquePacket) Parse() (p Packet, err error) {
|
||||
hdr := bytes.NewBuffer(nil)
|
||||
err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
return op, err
|
||||
}
|
||||
p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents)))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
p = op
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueReader reads OpaquePackets from an io.Reader.
|
||||
type OpaqueReader struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func NewOpaqueReader(r io.Reader) *OpaqueReader {
|
||||
return &OpaqueReader{r: r}
|
||||
}
|
||||
|
||||
// Read the next OpaquePacket.
|
||||
func (or *OpaqueReader) Next() (op *OpaquePacket, err error) {
|
||||
tag, _, contents, err := readHeader(or.r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
op = &OpaquePacket{Tag: uint8(tag), Reason: err}
|
||||
err = op.parse(contents)
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueSubpacket represents an unparsed OpenPGP subpacket,
|
||||
// as found in signature and user attribute packets.
|
||||
type OpaqueSubpacket struct {
|
||||
SubType uint8
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from
|
||||
// their byte representation.
|
||||
func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) {
|
||||
var (
|
||||
subHeaderLen int
|
||||
subPacket *OpaqueSubpacket
|
||||
)
|
||||
for len(contents) > 0 {
|
||||
subHeaderLen, subPacket, err = nextSubpacket(contents)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
result = append(result, subPacket)
|
||||
contents = contents[subHeaderLen+len(subPacket.Contents):]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) {
|
||||
// RFC 4880, section 5.2.3.1
|
||||
var subLen uint32
|
||||
if len(contents) < 1 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket = &OpaqueSubpacket{}
|
||||
switch {
|
||||
case contents[0] < 192:
|
||||
subHeaderLen = 2 // 1 length byte, 1 subtype byte
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[0])
|
||||
contents = contents[1:]
|
||||
case contents[0] < 255:
|
||||
subHeaderLen = 3 // 2 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192
|
||||
contents = contents[2:]
|
||||
default:
|
||||
subHeaderLen = 6 // 5 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[1])<<24 |
|
||||
uint32(contents[2])<<16 |
|
||||
uint32(contents[3])<<8 |
|
||||
uint32(contents[4])
|
||||
contents = contents[5:]
|
||||
}
|
||||
if subLen > uint32(len(contents)) || subLen == 0 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket.SubType = contents[0]
|
||||
subPacket.Contents = contents[1:subLen]
|
||||
return
|
||||
Truncated:
|
||||
err = errors.StructuralError("subpacket truncated")
|
||||
return
|
||||
}
|
||||
|
||||
func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) {
|
||||
buf := make([]byte, 6)
|
||||
n := serializeSubpacketLength(buf, len(osp.Contents)+1)
|
||||
buf[n] = osp.SubType
|
||||
if _, err = w.Write(buf[:n+1]); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(osp.Contents)
|
||||
return
|
||||
}
|
522
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
522
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
|
@ -1,522 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package packet implements parsing and serialization of OpenPGP packets, as
|
||||
// specified in RFC 4880.
|
||||
package packet // import "github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// readFull is the same as io.ReadFull except that reading zero bytes returns
|
||||
// ErrUnexpectedEOF rather than EOF.
|
||||
func readFull(r io.Reader, buf []byte) (n int, err error) {
|
||||
n, err = io.ReadFull(r, buf)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2.
|
||||
func readLength(r io.Reader) (length int64, isPartial bool, err error) {
|
||||
var buf [4]byte
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case buf[0] < 192:
|
||||
length = int64(buf[0])
|
||||
case buf[0] < 224:
|
||||
length = int64(buf[0]-192) << 8
|
||||
_, err = readFull(r, buf[0:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length += int64(buf[0]) + 192
|
||||
case buf[0] < 255:
|
||||
length = int64(1) << (buf[0] & 0x1f)
|
||||
isPartial = true
|
||||
default:
|
||||
_, err = readFull(r, buf[0:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length = int64(buf[0])<<24 |
|
||||
int64(buf[1])<<16 |
|
||||
int64(buf[2])<<8 |
|
||||
int64(buf[3])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths.
|
||||
// The continuation lengths are parsed and removed from the stream and EOF is
|
||||
// returned at the end of the packet. See RFC 4880, section 4.2.2.4.
|
||||
type partialLengthReader struct {
|
||||
r io.Reader
|
||||
remaining int64
|
||||
isPartial bool
|
||||
}
|
||||
|
||||
func (r *partialLengthReader) Read(p []byte) (n int, err error) {
|
||||
for r.remaining == 0 {
|
||||
if !r.isPartial {
|
||||
return 0, io.EOF
|
||||
}
|
||||
r.remaining, r.isPartial, err = readLength(r.r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
toRead := int64(len(p))
|
||||
if toRead > r.remaining {
|
||||
toRead = r.remaining
|
||||
}
|
||||
|
||||
n, err = r.r.Read(p[:int(toRead)])
|
||||
r.remaining -= int64(n)
|
||||
if n < int(toRead) && err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// partialLengthWriter writes a stream of data using OpenPGP partial lengths.
|
||||
// See RFC 4880, section 4.2.2.4.
|
||||
type partialLengthWriter struct {
|
||||
w io.WriteCloser
|
||||
buf bytes.Buffer
|
||||
lengthByte [1]byte
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Write(p []byte) (n int, err error) {
|
||||
bufLen := w.buf.Len()
|
||||
if bufLen > 512 {
|
||||
for power := uint(30); ; power-- {
|
||||
l := 1 << power
|
||||
if bufLen >= l {
|
||||
w.lengthByte[0] = 224 + uint8(power)
|
||||
_, err = w.w.Write(w.lengthByte[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var m int
|
||||
m, err = w.w.Write(w.buf.Next(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if m != l {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return w.buf.Write(p)
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Close() (err error) {
|
||||
len := w.buf.Len()
|
||||
err = serializeLength(w.w, len)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.buf.WriteTo(w.w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.w.Close()
|
||||
}
|
||||
|
||||
// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the
|
||||
// underlying Reader returns EOF before the limit has been reached.
|
||||
type spanReader struct {
|
||||
r io.Reader
|
||||
n int64
|
||||
}
|
||||
|
||||
func (l *spanReader) Read(p []byte) (n int, err error) {
|
||||
if l.n <= 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if int64(len(p)) > l.n {
|
||||
p = p[0:l.n]
|
||||
}
|
||||
n, err = l.r.Read(p)
|
||||
l.n -= int64(n)
|
||||
if l.n > 0 && err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readHeader parses a packet header and returns an io.Reader which will return
|
||||
// the contents of the packet. See RFC 4880, section 4.2.
|
||||
func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) {
|
||||
var buf [4]byte
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0]&0x80 == 0 {
|
||||
err = errors.StructuralError("tag byte does not have MSB set")
|
||||
return
|
||||
}
|
||||
if buf[0]&0x40 == 0 {
|
||||
// Old format packet
|
||||
tag = packetType((buf[0] & 0x3f) >> 2)
|
||||
lengthType := buf[0] & 3
|
||||
if lengthType == 3 {
|
||||
length = -1
|
||||
contents = r
|
||||
return
|
||||
}
|
||||
lengthBytes := 1 << lengthType
|
||||
_, err = readFull(r, buf[0:lengthBytes])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < lengthBytes; i++ {
|
||||
length <<= 8
|
||||
length |= int64(buf[i])
|
||||
}
|
||||
contents = &spanReader{r, length}
|
||||
return
|
||||
}
|
||||
|
||||
// New format packet
|
||||
tag = packetType(buf[0] & 0x3f)
|
||||
length, isPartial, err := readLength(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isPartial {
|
||||
contents = &partialLengthReader{
|
||||
remaining: length,
|
||||
isPartial: true,
|
||||
r: r,
|
||||
}
|
||||
length = -1
|
||||
} else {
|
||||
contents = &spanReader{r, length}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
|
||||
// 4.2.
|
||||
func serializeHeader(w io.Writer, ptype packetType, length int) (err error) {
|
||||
err = serializeType(w, ptype)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return serializeLength(w, length)
|
||||
}
|
||||
|
||||
// serializeType writes an OpenPGP packet type to w. See RFC 4880, section
|
||||
// 4.2.
|
||||
func serializeType(w io.Writer, ptype packetType) (err error) {
|
||||
var buf [1]byte
|
||||
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||||
_, err = w.Write(buf[:])
|
||||
return
|
||||
}
|
||||
|
||||
// serializeLength writes an OpenPGP packet length to w. See RFC 4880, section
|
||||
// 4.2.2.
|
||||
func serializeLength(w io.Writer, length int) (err error) {
|
||||
var buf [5]byte
|
||||
var n int
|
||||
|
||||
if length < 192 {
|
||||
buf[0] = byte(length)
|
||||
n = 1
|
||||
} else if length < 8384 {
|
||||
length -= 192
|
||||
buf[0] = 192 + byte(length>>8)
|
||||
buf[1] = byte(length)
|
||||
n = 2
|
||||
} else {
|
||||
buf[0] = 255
|
||||
buf[1] = byte(length >> 24)
|
||||
buf[2] = byte(length >> 16)
|
||||
buf[3] = byte(length >> 8)
|
||||
buf[4] = byte(length)
|
||||
n = 5
|
||||
}
|
||||
|
||||
_, err = w.Write(buf[:n])
|
||||
return
|
||||
}
|
||||
|
||||
// serializeStreamHeader writes an OpenPGP packet header to w where the
|
||||
// length of the packet is unknown. It returns a io.WriteCloser which can be
|
||||
// used to write the contents of the packet. See RFC 4880, section 4.2.
|
||||
func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) {
|
||||
err = serializeType(w, ptype)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = &partialLengthWriter{w: w}
|
||||
return
|
||||
}
|
||||
|
||||
// Packet represents an OpenPGP packet. Users are expected to try casting
|
||||
// instances of this interface to specific packet types.
|
||||
type Packet interface {
|
||||
parse(io.Reader) error
|
||||
}
|
||||
|
||||
// consumeAll reads from the given Reader until error, returning the number of
|
||||
// bytes read.
|
||||
func consumeAll(r io.Reader) (n int64, err error) {
|
||||
var m int
|
||||
var buf [1024]byte
|
||||
|
||||
for {
|
||||
m, err = r.Read(buf[:])
|
||||
n += int64(m)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// packetType represents the numeric ids of the different OpenPGP packet types. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2
|
||||
type packetType uint8
|
||||
|
||||
const (
|
||||
packetTypeEncryptedKey packetType = 1
|
||||
packetTypeSignature packetType = 2
|
||||
packetTypeSymmetricKeyEncrypted packetType = 3
|
||||
packetTypeOnePassSignature packetType = 4
|
||||
packetTypePrivateKey packetType = 5
|
||||
packetTypePublicKey packetType = 6
|
||||
packetTypePrivateSubkey packetType = 7
|
||||
packetTypeCompressed packetType = 8
|
||||
packetTypeSymmetricallyEncrypted packetType = 9
|
||||
packetTypeLiteralData packetType = 11
|
||||
packetTypeUserId packetType = 13
|
||||
packetTypePublicSubkey packetType = 14
|
||||
packetTypeUserAttribute packetType = 17
|
||||
packetTypeSymmetricallyEncryptedMDC packetType = 18
|
||||
packetTypeAEADEncrypted packetType = 20
|
||||
)
|
||||
|
||||
// EncryptedDataPacket holds encrypted data. It is currently implemented by
|
||||
// SymmetricallyEncrypted and AEADEncrypted.
|
||||
type EncryptedDataPacket interface {
|
||||
Decrypt(CipherFunction, []byte) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// Read reads a single OpenPGP packet from the given io.Reader. If there is an
|
||||
// error parsing a packet, the whole packet is consumed from the input.
|
||||
func Read(r io.Reader) (p Packet, err error) {
|
||||
tag, _, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case packetTypeEncryptedKey:
|
||||
p = new(EncryptedKey)
|
||||
case packetTypeSignature:
|
||||
p = new(Signature)
|
||||
case packetTypeSymmetricKeyEncrypted:
|
||||
p = new(SymmetricKeyEncrypted)
|
||||
case packetTypeOnePassSignature:
|
||||
p = new(OnePassSignature)
|
||||
case packetTypePrivateKey, packetTypePrivateSubkey:
|
||||
pk := new(PrivateKey)
|
||||
if tag == packetTypePrivateSubkey {
|
||||
pk.IsSubkey = true
|
||||
}
|
||||
p = pk
|
||||
case packetTypePublicKey, packetTypePublicSubkey:
|
||||
isSubkey := tag == packetTypePublicSubkey
|
||||
p = &PublicKey{IsSubkey: isSubkey}
|
||||
case packetTypeCompressed:
|
||||
p = new(Compressed)
|
||||
case packetTypeSymmetricallyEncrypted:
|
||||
err = errors.UnsupportedError("Symmetrically encrypted packets without MDC are not supported")
|
||||
case packetTypeLiteralData:
|
||||
p = new(LiteralData)
|
||||
case packetTypeUserId:
|
||||
p = new(UserId)
|
||||
case packetTypeUserAttribute:
|
||||
p = new(UserAttribute)
|
||||
case packetTypeSymmetricallyEncryptedMDC:
|
||||
se := new(SymmetricallyEncrypted)
|
||||
se.MDC = true
|
||||
p = se
|
||||
case packetTypeAEADEncrypted:
|
||||
p = new(AEADEncrypted)
|
||||
default:
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
}
|
||||
if p != nil {
|
||||
err = p.parse(contents)
|
||||
}
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SignatureType represents the different semantic meanings of an OpenPGP
|
||||
// signature. See RFC 4880, section 5.2.1.
|
||||
type SignatureType uint8
|
||||
|
||||
const (
|
||||
SigTypeBinary SignatureType = 0x00
|
||||
SigTypeText = 0x01
|
||||
SigTypeGenericCert = 0x10
|
||||
SigTypePersonaCert = 0x11
|
||||
SigTypeCasualCert = 0x12
|
||||
SigTypePositiveCert = 0x13
|
||||
SigTypeSubkeyBinding = 0x18
|
||||
SigTypePrimaryKeyBinding = 0x19
|
||||
SigTypeDirectSignature = 0x1F
|
||||
SigTypeKeyRevocation = 0x20
|
||||
SigTypeSubkeyRevocation = 0x28
|
||||
)
|
||||
|
||||
// PublicKeyAlgorithm represents the different public key system specified for
|
||||
// OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12
|
||||
type PublicKeyAlgorithm uint8
|
||||
|
||||
const (
|
||||
PubKeyAlgoRSA PublicKeyAlgorithm = 1
|
||||
PubKeyAlgoElGamal PublicKeyAlgorithm = 16
|
||||
PubKeyAlgoDSA PublicKeyAlgorithm = 17
|
||||
// RFC 6637, Section 5.
|
||||
PubKeyAlgoECDH PublicKeyAlgorithm = 18
|
||||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
|
||||
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
|
||||
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
|
||||
|
||||
// Deprecated in RFC 4880, Section 13.5. Use key flags instead.
|
||||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
|
||||
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
|
||||
)
|
||||
|
||||
// CanEncrypt returns true if it's possible to encrypt a message to a public
|
||||
// key of the given type.
|
||||
func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CanSign returns true if it's possible for a public key of the given type to
|
||||
// sign a message.
|
||||
func (pka PublicKeyAlgorithm) CanSign() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CipherFunction represents the different block ciphers specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
|
||||
type CipherFunction algorithm.CipherFunction
|
||||
|
||||
const (
|
||||
Cipher3DES CipherFunction = 2
|
||||
CipherCAST5 CipherFunction = 3
|
||||
CipherAES128 CipherFunction = 7
|
||||
CipherAES192 CipherFunction = 8
|
||||
CipherAES256 CipherFunction = 9
|
||||
)
|
||||
|
||||
// KeySize returns the key size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) KeySize() int {
|
||||
return algorithm.CipherFunction(cipher).KeySize()
|
||||
}
|
||||
|
||||
// blockSize returns the block size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) blockSize() int {
|
||||
return algorithm.CipherFunction(cipher).BlockSize()
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given cipher.
|
||||
func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
|
||||
return algorithm.CipherFunction(cipher).New(key)
|
||||
}
|
||||
|
||||
// padToKeySize left-pads a MPI with zeroes to match the length of the
|
||||
// specified RSA public.
|
||||
func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
if len(b) >= k {
|
||||
return b
|
||||
}
|
||||
bb := make([]byte, k)
|
||||
copy(bb[len(bb)-len(b):], b)
|
||||
return bb
|
||||
}
|
||||
|
||||
// CompressionAlgo Represents the different compression algorithms
|
||||
// supported by OpenPGP (except for BZIP2, which is not currently
|
||||
// supported). See Section 9.3 of RFC 4880.
|
||||
type CompressionAlgo uint8
|
||||
|
||||
const (
|
||||
CompressionNone CompressionAlgo = 0
|
||||
CompressionZIP CompressionAlgo = 1
|
||||
CompressionZLIB CompressionAlgo = 2
|
||||
)
|
||||
|
||||
// AEADMode represents the different Authenticated Encryption with Associated
|
||||
// Data specified for OpenPGP.
|
||||
type AEADMode algorithm.AEADMode
|
||||
|
||||
const (
|
||||
AEADModeEAX AEADMode = 1
|
||||
AEADModeOCB AEADMode = 2
|
||||
AEADModeExperimentalGCM AEADMode = 100
|
||||
)
|
||||
|
||||
func (mode AEADMode) NonceLength() int {
|
||||
return algorithm.AEADMode(mode).NonceLength()
|
||||
}
|
||||
|
||||
func (mode AEADMode) TagLength() int {
|
||||
return algorithm.AEADMode(mode).TagLength()
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given mode.
|
||||
func (mode AEADMode) new(block cipher.Block) cipher.AEAD {
|
||||
return algorithm.AEADMode(mode).New(block)
|
||||
}
|
||||
|
||||
// ReasonForRevocation represents a revocation reason code as per RFC4880
|
||||
// section 5.2.3.23.
|
||||
type ReasonForRevocation uint8
|
||||
|
||||
const (
|
||||
NoReason ReasonForRevocation = 0
|
||||
KeySuperseded ReasonForRevocation = 1
|
||||
KeyCompromised ReasonForRevocation = 2
|
||||
KeyRetired ReasonForRevocation = 3
|
||||
)
|
780
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
780
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
|
@ -1,780 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
// section 5.5.3.
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
|
||||
encryptedData []byte
|
||||
cipher CipherFunction
|
||||
s2k func(out, in []byte)
|
||||
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
|
||||
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
|
||||
PrivateKey interface{}
|
||||
sha1Checksum bool
|
||||
iv []byte
|
||||
|
||||
// Type of encryption of the S2K packet
|
||||
// Allowed values are 0 (Not encrypted), 254 (SHA1), or
|
||||
// 255 (2-byte checksum)
|
||||
s2kType S2KType
|
||||
// Full parameters of the S2K packet
|
||||
s2kParams *s2k.Params
|
||||
}
|
||||
|
||||
//S2KType s2k packet type
|
||||
type S2KType uint8
|
||||
|
||||
const (
|
||||
// S2KNON unencrypt
|
||||
S2KNON S2KType = 0
|
||||
// S2KSHA1 sha1 sum check
|
||||
S2KSHA1 S2KType = 254
|
||||
// S2KCHECKSUM sum check
|
||||
S2KCHECKSUM S2KType = 255
|
||||
)
|
||||
|
||||
func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEdDSAPrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pub := priv.Public().(ed25519.PublicKey)
|
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pub)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
|
||||
// implements RSA, ECDSA or EdDSA.
|
||||
func NewSignerPrivateKey(creationTime time.Time, signer crypto.Signer) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
// In general, the public Keys should be used as pointers. We still
|
||||
// type-switch on the values, for backwards-compatibility.
|
||||
switch pubkey := signer.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, pubkey)
|
||||
case rsa.PublicKey:
|
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey)
|
||||
case *ecdsa.PublicKey:
|
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, pubkey)
|
||||
case ecdsa.PublicKey:
|
||||
pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey)
|
||||
case *ed25519.PublicKey:
|
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, pubkey)
|
||||
case ed25519.PublicKey:
|
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey)
|
||||
default:
|
||||
panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey")
|
||||
}
|
||||
pk.PrivateKey = signer
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
|
||||
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
switch priv := decrypter.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
|
||||
case *elgamal.PrivateKey:
|
||||
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
|
||||
case *ecdh.PrivateKey:
|
||||
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
|
||||
default:
|
||||
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey")
|
||||
}
|
||||
pk.PrivateKey = decrypter
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
err = (&pk.PublicKey).parse(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
v5 := pk.PublicKey.Version == 5
|
||||
|
||||
var buf [1]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.s2kType = S2KType(buf[0])
|
||||
var optCount [1]byte
|
||||
if v5 {
|
||||
if _, err = readFull(r, optCount[:]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch pk.s2kType {
|
||||
case S2KNON:
|
||||
pk.s2k = nil
|
||||
pk.Encrypted = false
|
||||
case S2KSHA1, S2KCHECKSUM:
|
||||
if v5 && pk.s2kType == S2KCHECKSUM {
|
||||
return errors.StructuralError("wrong s2k identifier for version 5")
|
||||
}
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.cipher = CipherFunction(buf[0])
|
||||
pk.s2kParams, err = s2k.ParseIntoParams(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if pk.s2kParams.Dummy() {
|
||||
return
|
||||
}
|
||||
pk.s2k, err = pk.s2kParams.Function()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.Encrypted = true
|
||||
if pk.s2kType == S2KSHA1 {
|
||||
pk.sha1Checksum = true
|
||||
}
|
||||
default:
|
||||
return errors.UnsupportedError("deprecated s2k function in private key")
|
||||
}
|
||||
|
||||
if pk.Encrypted {
|
||||
blockSize := pk.cipher.blockSize()
|
||||
if blockSize == 0 {
|
||||
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
|
||||
}
|
||||
pk.iv = make([]byte, blockSize)
|
||||
_, err = readFull(r, pk.iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var privateKeyData []byte
|
||||
if v5 {
|
||||
var n [4]byte /* secret material four octet count */
|
||||
_, err = readFull(r, n[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
count := uint32(uint32(n[0])<<24 | uint32(n[1])<<16 | uint32(n[2])<<8 | uint32(n[3]))
|
||||
if !pk.Encrypted {
|
||||
count = count + 2 /* two octet checksum */
|
||||
}
|
||||
privateKeyData = make([]byte, count)
|
||||
_, err = readFull(r, privateKeyData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
privateKeyData, err = ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !pk.Encrypted {
|
||||
return pk.parsePrivateKey(privateKeyData)
|
||||
}
|
||||
|
||||
pk.encryptedData = privateKeyData
|
||||
return
|
||||
}
|
||||
|
||||
// Dummy returns true if the private key is a dummy key. This is a GNU extension.
|
||||
func (pk *PrivateKey) Dummy() bool {
|
||||
return pk.s2kParams.Dummy()
|
||||
}
|
||||
|
||||
func mod64kHash(d []byte) uint16 {
|
||||
var h uint16
|
||||
for _, b := range d {
|
||||
h += uint16(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
||||
contents := bytes.NewBuffer(nil)
|
||||
err = pk.PublicKey.serializeWithoutHeaders(contents)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = contents.Write([]byte{uint8(pk.s2kType)}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
optional := bytes.NewBuffer(nil)
|
||||
if pk.Encrypted || pk.Dummy() {
|
||||
optional.Write([]byte{uint8(pk.cipher)})
|
||||
if err := pk.s2kParams.Serialize(optional); err != nil {
|
||||
return err
|
||||
}
|
||||
if pk.Encrypted {
|
||||
optional.Write(pk.iv)
|
||||
}
|
||||
}
|
||||
if pk.Version == 5 {
|
||||
contents.Write([]byte{uint8(optional.Len())})
|
||||
}
|
||||
io.Copy(contents, optional)
|
||||
|
||||
if !pk.Dummy() {
|
||||
l := 0
|
||||
var priv []byte
|
||||
if !pk.Encrypted {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err = pk.serializePrivateKey(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l = buf.Len()
|
||||
if pk.sha1Checksum {
|
||||
h := sha1.New()
|
||||
h.Write(buf.Bytes())
|
||||
buf.Write(h.Sum(nil))
|
||||
} else {
|
||||
checksum := mod64kHash(buf.Bytes())
|
||||
buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
|
||||
}
|
||||
priv = buf.Bytes()
|
||||
} else {
|
||||
priv, l = pk.encryptedData, len(pk.encryptedData)
|
||||
}
|
||||
|
||||
if pk.Version == 5 {
|
||||
contents.Write([]byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)})
|
||||
}
|
||||
contents.Write(priv)
|
||||
}
|
||||
|
||||
ptype := packetTypePrivateKey
|
||||
if pk.IsSubkey {
|
||||
ptype = packetTypePrivateSubkey
|
||||
}
|
||||
err = serializeHeader(w, ptype, contents.Len())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(w, contents)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
|
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
|
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
|
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
|
||||
_, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEdDSAPrivateKey(w io.Writer, priv *ed25519.PrivateKey) error {
|
||||
keySize := ed25519.PrivateKeySize - ed25519.PublicKeySize
|
||||
_, err := w.Write(encoding.NewMPI((*priv)[:keySize]).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error {
|
||||
_, err := w.Write(encoding.NewMPI(priv.D).EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
||||
if pk.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
if !pk.Encrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := make([]byte, pk.cipher.KeySize())
|
||||
pk.s2k(key, passphrase)
|
||||
block := pk.cipher.new(key)
|
||||
cfb := cipher.NewCFBDecrypter(block, pk.iv)
|
||||
|
||||
data := make([]byte, len(pk.encryptedData))
|
||||
cfb.XORKeyStream(data, pk.encryptedData)
|
||||
|
||||
if pk.sha1Checksum {
|
||||
if len(data) < sha1.Size {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(data[:len(data)-sha1.Size])
|
||||
sum := h.Sum(nil)
|
||||
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-sha1.Size]
|
||||
} else {
|
||||
if len(data) < 2 {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(data)-2; i++ {
|
||||
sum += uint16(data[i])
|
||||
}
|
||||
if data[len(data)-2] != uint8(sum>>8) ||
|
||||
data[len(data)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-2]
|
||||
}
|
||||
|
||||
err := pk.parsePrivateKey(data)
|
||||
if _, ok := err.(errors.KeyInvalidError); ok {
|
||||
return errors.KeyInvalidError("invalid key parameters")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark key as unencrypted
|
||||
pk.s2kType = S2KNON
|
||||
pk.s2k = nil
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts an unencrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Encrypt(passphrase []byte) error {
|
||||
priv := bytes.NewBuffer(nil)
|
||||
err := pk.serializePrivateKey(priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Default config of private key encryption
|
||||
pk.cipher = CipherAES256
|
||||
s2kConfig := &s2k.Config{
|
||||
S2KMode: 3, //Iterated
|
||||
S2KCount: 65536,
|
||||
Hash: crypto.SHA256,
|
||||
}
|
||||
|
||||
pk.s2kParams, err = s2k.Generate(rand.Reader, s2kConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privateKeyBytes := priv.Bytes()
|
||||
key := make([]byte, pk.cipher.KeySize())
|
||||
|
||||
pk.sha1Checksum = true
|
||||
pk.s2k, err = pk.s2kParams.Function()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.s2k(key, passphrase)
|
||||
block := pk.cipher.new(key)
|
||||
pk.iv = make([]byte, pk.cipher.blockSize())
|
||||
_, err = rand.Read(pk.iv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfb := cipher.NewCFBEncrypter(block, pk.iv)
|
||||
|
||||
if pk.sha1Checksum {
|
||||
pk.s2kType = S2KSHA1
|
||||
h := sha1.New()
|
||||
h.Write(privateKeyBytes)
|
||||
sum := h.Sum(nil)
|
||||
privateKeyBytes = append(privateKeyBytes, sum...)
|
||||
} else {
|
||||
pk.s2kType = S2KCHECKSUM
|
||||
var sum uint16
|
||||
for _, b := range privateKeyBytes {
|
||||
sum += uint16(b)
|
||||
}
|
||||
priv.Write([]byte{uint8(sum >> 8), uint8(sum)})
|
||||
}
|
||||
|
||||
pk.encryptedData = make([]byte, len(privateKeyBytes))
|
||||
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
|
||||
pk.Encrypted = true
|
||||
pk.PrivateKey = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) {
|
||||
switch priv := pk.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
err = serializeRSAPrivateKey(w, priv)
|
||||
case *dsa.PrivateKey:
|
||||
err = serializeDSAPrivateKey(w, priv)
|
||||
case *elgamal.PrivateKey:
|
||||
err = serializeElGamalPrivateKey(w, priv)
|
||||
case *ecdsa.PrivateKey:
|
||||
err = serializeECDSAPrivateKey(w, priv)
|
||||
case *ed25519.PrivateKey:
|
||||
err = serializeEdDSAPrivateKey(w, priv)
|
||||
case *ecdh.PrivateKey:
|
||||
err = serializeECDHPrivateKey(w, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
|
||||
switch pk.PublicKey.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
|
||||
return pk.parseRSAPrivateKey(data)
|
||||
case PubKeyAlgoDSA:
|
||||
return pk.parseDSAPrivateKey(data)
|
||||
case PubKeyAlgoElGamal:
|
||||
return pk.parseElGamalPrivateKey(data)
|
||||
case PubKeyAlgoECDSA:
|
||||
return pk.parseECDSAPrivateKey(data)
|
||||
case PubKeyAlgoECDH:
|
||||
return pk.parseECDHPrivateKey(data)
|
||||
case PubKeyAlgoEdDSA:
|
||||
return pk.parseEdDSAPrivateKey(data)
|
||||
}
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
|
||||
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
|
||||
rsaPriv := new(rsa.PrivateKey)
|
||||
rsaPriv.PublicKey = *rsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d := new(encoding.MPI)
|
||||
if _, err := d.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := new(encoding.MPI)
|
||||
if _, err := p.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := new(encoding.MPI)
|
||||
if _, err := q.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsaPriv.D = new(big.Int).SetBytes(d.Bytes())
|
||||
rsaPriv.Primes = make([]*big.Int, 2)
|
||||
rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes())
|
||||
rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes())
|
||||
if err := rsaPriv.Validate(); err != nil {
|
||||
return errors.KeyInvalidError(err.Error())
|
||||
}
|
||||
rsaPriv.Precompute()
|
||||
pk.PrivateKey = rsaPriv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) {
|
||||
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
|
||||
dsaPriv := new(dsa.PrivateKey)
|
||||
dsaPriv.PublicKey = *dsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
x := new(encoding.MPI)
|
||||
if _, err := x.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dsaPriv.X = new(big.Int).SetBytes(x.Bytes())
|
||||
if err := validateDSAParameters(dsaPriv); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = dsaPriv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
|
||||
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey)
|
||||
priv := new(elgamal.PrivateKey)
|
||||
priv.PublicKey = *pub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
x := new(encoding.MPI)
|
||||
if _, err := x.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
priv.X = new(big.Int).SetBytes(x.Bytes())
|
||||
if err := validateElGamalParameters(priv); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = priv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
||||
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
|
||||
ecdsaPriv := new(ecdsa.PrivateKey)
|
||||
ecdsaPriv.PublicKey = *ecdsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d := new(encoding.MPI)
|
||||
if _, err := d.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ecdsaPriv.D = new(big.Int).SetBytes(d.Bytes())
|
||||
if err := validateECDSAParameters(ecdsaPriv); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = ecdsaPriv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) {
|
||||
ecdhPub := pk.PublicKey.PublicKey.(*ecdh.PublicKey)
|
||||
ecdhPriv := new(ecdh.PrivateKey)
|
||||
ecdhPriv.PublicKey = *ecdhPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d := new(encoding.MPI)
|
||||
if _, err := d.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ecdhPriv.D = d.Bytes()
|
||||
if err := validateECDHParameters(ecdhPriv); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = ecdhPriv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
|
||||
eddsaPub := pk.PublicKey.PublicKey.(*ed25519.PublicKey)
|
||||
eddsaPriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d := new(encoding.MPI)
|
||||
if _, err := d.ReadFrom(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
priv := d.Bytes()
|
||||
copy(eddsaPriv[32-len(priv):32], priv)
|
||||
copy(eddsaPriv[32:], (*eddsaPub)[:])
|
||||
if err := validateEdDSAParameters(&eddsaPriv); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = &eddsaPriv
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateECDSAParameters(priv *ecdsa.PrivateKey) error {
|
||||
return validateCommonECC(priv.Curve, priv.D.Bytes(), priv.X, priv.Y)
|
||||
}
|
||||
|
||||
func validateECDHParameters(priv *ecdh.PrivateKey) error {
|
||||
if priv.CurveType != ecc.Curve25519 {
|
||||
return validateCommonECC(priv.Curve, priv.D, priv.X, priv.Y)
|
||||
}
|
||||
// Handle Curve25519
|
||||
Q := priv.X.Bytes()[1:]
|
||||
var d [32]byte
|
||||
// Copy reversed d
|
||||
l := len(priv.D)
|
||||
for i := 0; i < l; i++ {
|
||||
d[i] = priv.D[l-i-1]
|
||||
}
|
||||
var expectedQ [32]byte
|
||||
curve25519.ScalarBaseMult(&expectedQ, &d)
|
||||
if !bytes.Equal(Q, expectedQ[:]) {
|
||||
return errors.KeyInvalidError("ECDH curve25519: invalid point")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCommonECC(curve elliptic.Curve, d []byte, X, Y *big.Int) error {
|
||||
// the public point should not be at infinity (0,0)
|
||||
zero := new(big.Int)
|
||||
if X.Cmp(zero) == 0 && Y.Cmp(zero) == 0 {
|
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", curve.Params().Name))
|
||||
}
|
||||
// re-derive the public point Q' = (X,Y) = dG
|
||||
// to compare to declared Q in public key
|
||||
expectedX, expectedY := curve.ScalarBaseMult(d)
|
||||
if X.Cmp(expectedX) != 0 || Y.Cmp(expectedY) != 0 {
|
||||
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", curve.Params().Name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEdDSAParameters(priv *ed25519.PrivateKey) error {
|
||||
// In EdDSA, the serialized public point is stored as part of private key (together with the seed),
|
||||
// hence we can re-derive the key from the seed
|
||||
seed := priv.Seed()
|
||||
expectedPriv := ed25519.NewKeyFromSeed(seed)
|
||||
if !bytes.Equal(*priv, expectedPriv) {
|
||||
return errors.KeyInvalidError("eddsa: invalid point")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDSAParameters(priv *dsa.PrivateKey) error {
|
||||
p := priv.P // group prime
|
||||
q := priv.Q // subgroup order
|
||||
g := priv.G // g has order q mod p
|
||||
x := priv.X // secret
|
||||
y := priv.Y // y == g**x mod p
|
||||
one := big.NewInt(1)
|
||||
// expect g, y >= 2 and g < p
|
||||
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
|
||||
return errors.KeyInvalidError("dsa: invalid group")
|
||||
}
|
||||
// expect p > q
|
||||
if p.Cmp(q) <= 0 {
|
||||
return errors.KeyInvalidError("dsa: invalid group prime")
|
||||
}
|
||||
// q should be large enough and divide p-1
|
||||
pSub1 := new(big.Int).Sub(p, one)
|
||||
if q.BitLen() < 150 || new(big.Int).Mod(pSub1, q).Cmp(big.NewInt(0)) != 0 {
|
||||
return errors.KeyInvalidError("dsa: invalid order")
|
||||
}
|
||||
// confirm that g has order q mod p
|
||||
if !q.ProbablyPrime(32) || new(big.Int).Exp(g, q, p).Cmp(one) != 0 {
|
||||
return errors.KeyInvalidError("dsa: invalid order")
|
||||
}
|
||||
// check y
|
||||
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
|
||||
return errors.KeyInvalidError("dsa: mismatching values")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateElGamalParameters(priv *elgamal.PrivateKey) error {
|
||||
p := priv.P // group prime
|
||||
g := priv.G // g has order p-1 mod p
|
||||
x := priv.X // secret
|
||||
y := priv.Y // y == g**x mod p
|
||||
one := big.NewInt(1)
|
||||
// Expect g, y >= 2 and g < p
|
||||
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
|
||||
return errors.KeyInvalidError("elgamal: invalid group")
|
||||
}
|
||||
if p.BitLen() < 1024 {
|
||||
return errors.KeyInvalidError("elgamal: group order too small")
|
||||
}
|
||||
pSub1 := new(big.Int).Sub(p, one)
|
||||
if new(big.Int).Exp(g, pSub1, p).Cmp(one) != 0 {
|
||||
return errors.KeyInvalidError("elgamal: invalid group")
|
||||
}
|
||||
// Since p-1 is not prime, g might have a smaller order that divides p-1.
|
||||
// We cannot confirm the exact order of g, but we make sure it is not too small.
|
||||
gExpI := new(big.Int).Set(g)
|
||||
i := 1
|
||||
threshold := 2 << 17 // we want order > threshold
|
||||
for i < threshold {
|
||||
i++ // we check every order to make sure key validation is not easily bypassed by guessing y'
|
||||
gExpI.Mod(new(big.Int).Mul(gExpI, g), p)
|
||||
if gExpI.Cmp(one) == 0 {
|
||||
return errors.KeyInvalidError("elgamal: order too small")
|
||||
}
|
||||
}
|
||||
// Check y
|
||||
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
|
||||
return errors.KeyInvalidError("elgamal: mismatching values")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.go
generated
vendored
|
@ -1,12 +0,0 @@
|
|||
package packet
|
||||
|
||||
// Generated with `gpg --export-secret-keys "Test Key 2"`
|
||||
const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec"
|
||||
|
||||
// Generated by `gpg --export-secret-keys` followed by a manual extraction of
|
||||
// the ElGamal subkey from the packets.
|
||||
const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc"
|
||||
|
||||
// pkcs1PrivKeyHex is a PKCS#1, RSA private key.
|
||||
// Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p`
|
||||
const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7"
|
825
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
825
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
|
@ -1,825 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
type kdfHashFunction byte
|
||||
type kdfAlgorithm byte
|
||||
|
||||
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
|
||||
type PublicKey struct {
|
||||
Version int
|
||||
CreationTime time.Time
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey
|
||||
Fingerprint []byte
|
||||
KeyId uint64
|
||||
IsSubkey bool
|
||||
|
||||
// RFC 4880 fields
|
||||
n, e, p, q, g, y encoding.Field
|
||||
|
||||
// RFC 6637 fields
|
||||
// oid contains the OID byte sequence identifying the elliptic curve used
|
||||
oid encoding.Field
|
||||
|
||||
// kdf stores key derivation function parameters
|
||||
// used for ECDH encryption. See RFC 6637, Section 9.
|
||||
kdf encoding.Field
|
||||
}
|
||||
|
||||
// UpgradeToV5 updates the version of the key to v5, and updates all necessary
|
||||
// fields.
|
||||
func (pk *PublicKey) UpgradeToV5() {
|
||||
pk.Version = 5
|
||||
pk.setFingerprintAndKeyId()
|
||||
}
|
||||
|
||||
// signingKey provides a convenient abstraction over signature verification
|
||||
// for v3 and v4 public keys.
|
||||
type signingKey interface {
|
||||
SerializeForHash(io.Writer) error
|
||||
SerializeSignaturePrefix(io.Writer)
|
||||
serializeWithoutHeaders(io.Writer) error
|
||||
}
|
||||
|
||||
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
|
||||
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoRSA,
|
||||
PublicKey: pub,
|
||||
n: new(encoding.MPI).SetBig(pub.N),
|
||||
e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))),
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey.
|
||||
func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoDSA,
|
||||
PublicKey: pub,
|
||||
p: new(encoding.MPI).SetBig(pub.P),
|
||||
q: new(encoding.MPI).SetBig(pub.Q),
|
||||
g: new(encoding.MPI).SetBig(pub.G),
|
||||
y: new(encoding.MPI).SetBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
|
||||
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoElGamal,
|
||||
PublicKey: pub,
|
||||
p: new(encoding.MPI).SetBig(pub.P),
|
||||
g: new(encoding.MPI).SetBig(pub.G),
|
||||
y: new(encoding.MPI).SetBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoECDSA,
|
||||
PublicKey: pub,
|
||||
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)),
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByCurve(pub.Curve)
|
||||
if curveInfo == nil {
|
||||
panic("unknown elliptic curve")
|
||||
}
|
||||
pk.oid = curveInfo.Oid
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey {
|
||||
var pk *PublicKey
|
||||
var curveInfo *ecc.CurveInfo
|
||||
var kdf = encoding.NewOID([]byte{0x1, pub.Hash.Id(), pub.Cipher.Id()})
|
||||
if pub.CurveType == ecc.Curve25519 {
|
||||
pk = &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoECDH,
|
||||
PublicKey: pub,
|
||||
p: encoding.NewMPI(pub.X.Bytes()),
|
||||
kdf: kdf,
|
||||
}
|
||||
curveInfo = ecc.FindByName("Curve25519")
|
||||
} else {
|
||||
pk = &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoECDH,
|
||||
PublicKey: pub,
|
||||
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)),
|
||||
kdf: kdf,
|
||||
}
|
||||
curveInfo = ecc.FindByCurve(pub.Curve)
|
||||
}
|
||||
if curveInfo == nil {
|
||||
panic("unknown elliptic curve")
|
||||
}
|
||||
pk.oid = curveInfo.Oid
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEdDSAPublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey {
|
||||
curveInfo := ecc.FindByName("Ed25519")
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoEdDSA,
|
||||
PublicKey: pub,
|
||||
oid: curveInfo.Oid,
|
||||
// Native point format, see draft-koch-eddsa-for-openpgp-04, Appendix B
|
||||
p: encoding.NewMPI(append([]byte{0x40}, *pub...)),
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 && buf[0] != 5 {
|
||||
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
pk.Version = int(buf[0])
|
||||
if pk.Version == 5 {
|
||||
var n [4]byte
|
||||
_, err = readFull(r, n[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
|
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
err = pk.parseRSA(r)
|
||||
case PubKeyAlgoDSA:
|
||||
err = pk.parseDSA(r)
|
||||
case PubKeyAlgoElGamal:
|
||||
err = pk.parseElGamal(r)
|
||||
case PubKeyAlgoECDSA:
|
||||
err = pk.parseECDSA(r)
|
||||
case PubKeyAlgoECDH:
|
||||
err = pk.parseECDH(r)
|
||||
case PubKeyAlgoEdDSA:
|
||||
err = pk.parseEdDSA(r)
|
||||
default:
|
||||
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) setFingerprintAndKeyId() {
|
||||
// RFC 4880, section 12.2
|
||||
if pk.Version == 5 {
|
||||
fingerprint := sha256.New()
|
||||
pk.SerializeForHash(fingerprint)
|
||||
pk.Fingerprint = make([]byte, 32)
|
||||
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8])
|
||||
} else {
|
||||
fingerprint := sha1.New()
|
||||
pk.SerializeForHash(fingerprint)
|
||||
pk.Fingerprint = make([]byte, 20)
|
||||
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
|
||||
}
|
||||
}
|
||||
|
||||
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
||||
pk.n = new(encoding.MPI)
|
||||
if _, err = pk.n.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.e = new(encoding.MPI)
|
||||
if _, err = pk.e.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(pk.e.Bytes()) > 3 {
|
||||
err = errors.UnsupportedError("large public exponent")
|
||||
return
|
||||
}
|
||||
rsa := &rsa.PublicKey{
|
||||
N: new(big.Int).SetBytes(pk.n.Bytes()),
|
||||
E: 0,
|
||||
}
|
||||
for i := 0; i < len(pk.e.Bytes()); i++ {
|
||||
rsa.E <<= 8
|
||||
rsa.E |= int(pk.e.Bytes()[i])
|
||||
}
|
||||
pk.PublicKey = rsa
|
||||
return
|
||||
}
|
||||
|
||||
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseDSA(r io.Reader) (err error) {
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.q = new(encoding.MPI)
|
||||
if _, err = pk.q.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.g = new(encoding.MPI)
|
||||
if _, err = pk.g.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.y = new(encoding.MPI)
|
||||
if _, err = pk.y.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dsa := new(dsa.PublicKey)
|
||||
dsa.P = new(big.Int).SetBytes(pk.p.Bytes())
|
||||
dsa.Q = new(big.Int).SetBytes(pk.q.Bytes())
|
||||
dsa.G = new(big.Int).SetBytes(pk.g.Bytes())
|
||||
dsa.Y = new(big.Int).SetBytes(pk.y.Bytes())
|
||||
pk.PublicKey = dsa
|
||||
return
|
||||
}
|
||||
|
||||
// parseElGamal parses ElGamal public key material from the given Reader. See
|
||||
// RFC 4880, section 5.5.2.
|
||||
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) {
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.g = new(encoding.MPI)
|
||||
if _, err = pk.g.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.y = new(encoding.MPI)
|
||||
if _, err = pk.y.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
elgamal := new(elgamal.PublicKey)
|
||||
elgamal.P = new(big.Int).SetBytes(pk.p.Bytes())
|
||||
elgamal.G = new(big.Int).SetBytes(pk.g.Bytes())
|
||||
elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes())
|
||||
pk.PublicKey = elgamal
|
||||
return
|
||||
}
|
||||
|
||||
// parseECDSA parses ECDSA public key material from the given Reader. See
|
||||
// RFC 6637, Section 9.
|
||||
func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
|
||||
pk.oid = new(encoding.OID)
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var c elliptic.Curve
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.ECDSA {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
||||
}
|
||||
c = curveInfo.Curve
|
||||
x, y := elliptic.Unmarshal(c, pk.p.Bytes())
|
||||
if x == nil {
|
||||
return errors.UnsupportedError("failed to parse EC point")
|
||||
}
|
||||
pk.PublicKey = &ecdsa.PublicKey{Curve: c, X: x, Y: y}
|
||||
return
|
||||
}
|
||||
|
||||
// parseECDH parses ECDH public key material from the given Reader. See
|
||||
// RFC 6637, Section 9.
|
||||
func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
|
||||
pk.oid = new(encoding.OID)
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.kdf = new(encoding.OID)
|
||||
if _, err = pk.kdf.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
||||
}
|
||||
|
||||
c := curveInfo.Curve
|
||||
cType := curveInfo.CurveType
|
||||
|
||||
var x, y *big.Int
|
||||
if cType == ecc.Curve25519 {
|
||||
x = new(big.Int)
|
||||
x.SetBytes(pk.p.Bytes())
|
||||
} else {
|
||||
x, y = elliptic.Unmarshal(c, pk.p.Bytes())
|
||||
}
|
||||
if x == nil {
|
||||
return errors.UnsupportedError("failed to parse EC point")
|
||||
}
|
||||
|
||||
if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 {
|
||||
return errors.UnsupportedError("unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
|
||||
}
|
||||
if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 {
|
||||
return errors.UnsupportedError("unsupported KDF reserved field: " + strconv.Itoa(int(reserved)))
|
||||
}
|
||||
kdfHash, ok := algorithm.HashById[pk.kdf.Bytes()[1]]
|
||||
if !ok {
|
||||
return errors.UnsupportedError("unsupported ECDH KDF hash: " + strconv.Itoa(int(pk.kdf.Bytes()[1])))
|
||||
}
|
||||
kdfCipher, ok := algorithm.CipherById[pk.kdf.Bytes()[2]]
|
||||
if !ok {
|
||||
return errors.UnsupportedError("unsupported ECDH KDF cipher: " + strconv.Itoa(int(pk.kdf.Bytes()[2])))
|
||||
}
|
||||
|
||||
pk.PublicKey = &ecdh.PublicKey{
|
||||
CurveType: cType,
|
||||
Curve: c,
|
||||
X: x,
|
||||
Y: y,
|
||||
KDF: ecdh.KDF{
|
||||
Hash: kdfHash,
|
||||
Cipher: kdfCipher,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
|
||||
pk.oid = new(encoding.OID)
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.EdDSA {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
||||
}
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
eddsa := make(ed25519.PublicKey, ed25519.PublicKeySize)
|
||||
switch flag := pk.p.Bytes()[0]; flag {
|
||||
case 0x04:
|
||||
// TODO: see _grcy_ecc_eddsa_ensure_compact in grcypt
|
||||
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
|
||||
case 0x40:
|
||||
copy(eddsa[:], pk.p.Bytes()[1:])
|
||||
default:
|
||||
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
|
||||
}
|
||||
|
||||
pk.PublicKey = &eddsa
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeForHash serializes the PublicKey to w with the special packet
|
||||
// header format needed for hashing.
|
||||
func (pk *PublicKey) SerializeForHash(w io.Writer) error {
|
||||
pk.SerializeSignaturePrefix(w)
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
||||
// The prefix is used when calculating a signature over this public key. See
|
||||
// RFC 4880, section 5.2.4.
|
||||
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) {
|
||||
var pLength = pk.algorithmSpecificByteCount()
|
||||
if pk.Version == 5 {
|
||||
pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
|
||||
w.Write([]byte{
|
||||
0x9A,
|
||||
byte(pLength >> 24),
|
||||
byte(pLength >> 16),
|
||||
byte(pLength >> 8),
|
||||
byte(pLength),
|
||||
})
|
||||
return
|
||||
}
|
||||
pLength += 6
|
||||
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
|
||||
}
|
||||
|
||||
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
|
||||
length := 6 // 6 byte header
|
||||
length += pk.algorithmSpecificByteCount()
|
||||
if pk.Version == 5 {
|
||||
length += 4 // octet key count
|
||||
}
|
||||
packetType := packetTypePublicKey
|
||||
if pk.IsSubkey {
|
||||
packetType = packetTypePublicSubkey
|
||||
}
|
||||
err = serializeHeader(w, packetType, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) algorithmSpecificByteCount() int {
|
||||
length := 0
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
length += int(pk.n.EncodedLength())
|
||||
length += int(pk.e.EncodedLength())
|
||||
case PubKeyAlgoDSA:
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.q.EncodedLength())
|
||||
length += int(pk.g.EncodedLength())
|
||||
length += int(pk.y.EncodedLength())
|
||||
case PubKeyAlgoElGamal:
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.g.EncodedLength())
|
||||
length += int(pk.y.EncodedLength())
|
||||
case PubKeyAlgoECDSA:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
case PubKeyAlgoECDH:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.kdf.EncodedLength())
|
||||
case PubKeyAlgoEdDSA:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
|
||||
// OpenPGP public key packet, not including the packet header.
|
||||
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
||||
t := uint32(pk.CreationTime.Unix())
|
||||
if _, err = w.Write([]byte{
|
||||
byte(pk.Version),
|
||||
byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t),
|
||||
byte(pk.PubKeyAlgo),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if pk.Version == 5 {
|
||||
n := pk.algorithmSpecificByteCount()
|
||||
if _, err = w.Write([]byte{
|
||||
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
if _, err = w.Write(pk.n.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.e.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoDSA:
|
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(pk.q.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.y.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoElGamal:
|
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.y.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoECDSA:
|
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.p.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoECDH:
|
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.kdf.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoEdDSA:
|
||||
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(pk.p.EncodedBytes())
|
||||
return
|
||||
}
|
||||
return errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
|
||||
// CanSign returns true iff this public key can generate signatures
|
||||
func (pk *PublicKey) CanSign() bool {
|
||||
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH
|
||||
}
|
||||
|
||||
// VerifySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) {
|
||||
if !pk.CanSign() {
|
||||
return errors.InvalidArgumentError("public key cannot generate signatures")
|
||||
}
|
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
|
||||
sig.AddMetadataToHashSuffix()
|
||||
}
|
||||
signed.Write(sig.HashSuffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
|
||||
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("public key and signature use different algorithms")
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
|
||||
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.Bytes()))
|
||||
if err != nil {
|
||||
return errors.SignatureError("RSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoDSA:
|
||||
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
|
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8
|
||||
if len(hashBytes) > subgroupSize {
|
||||
hashBytes = hashBytes[:subgroupSize]
|
||||
}
|
||||
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) {
|
||||
return errors.SignatureError("DSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoECDSA:
|
||||
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey)
|
||||
if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) {
|
||||
return errors.SignatureError("ECDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoEdDSA:
|
||||
eddsaPublicKey := pk.PublicKey.(*ed25519.PublicKey)
|
||||
|
||||
sigR := sig.EdDSASigR.Bytes()
|
||||
sigS := sig.EdDSASigS.Bytes()
|
||||
|
||||
eddsaSig := make([]byte, ed25519.SignatureSize)
|
||||
copy(eddsaSig[32-len(sigR):32], sigR)
|
||||
copy(eddsaSig[64-len(sigS):], sigS)
|
||||
|
||||
if !ed25519.Verify(*eddsaPublicKey, hashBytes, eddsaSig) {
|
||||
return errors.SignatureError("EdDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.SignatureError("Unsupported public key algorithm used in signature")
|
||||
}
|
||||
}
|
||||
|
||||
// keySignatureHash returns a Hash of the message that needs to be signed for
|
||||
// pk to assert a subkey relationship to signed.
|
||||
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = signed.SerializeForHash(h)
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of signed.
|
||||
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error {
|
||||
h, err := keySignatureHash(pk, signed, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = pk.VerifySignature(h, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sig.FlagSign {
|
||||
// Signing subkeys must be cross-signed. See
|
||||
// https://www.gnupg.org/faq/subkey-cross-certify.html.
|
||||
if sig.EmbeddedSignature == nil {
|
||||
return errors.StructuralError("signing subkey is missing cross-signature")
|
||||
}
|
||||
// Verify the cross-signature. This is calculated over the same
|
||||
// data as the main signature, so we cannot just recursively
|
||||
// call signed.VerifyKeySignature(...)
|
||||
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil {
|
||||
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
|
||||
}
|
||||
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
|
||||
return errors.StructuralError("error while verifying cross-signature: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key.
|
||||
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
|
||||
h, err := keyRevocationHash(pk, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
|
||||
// made by the passed in signingKey.
|
||||
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signingKey *PublicKey) (err error) {
|
||||
h, err := keyRevocationHash(pk, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return signingKey.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// userIdSignatureHash returns a Hash of the message that needs to be signed
|
||||
// to assert that pk is a valid key for id.
|
||||
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
|
||||
var buf [5]byte
|
||||
buf[0] = 0xb4
|
||||
buf[1] = byte(len(id) >> 24)
|
||||
buf[2] = byte(len(id) >> 16)
|
||||
buf[3] = byte(len(id) >> 8)
|
||||
buf[4] = byte(len(id))
|
||||
h.Write(buf[:])
|
||||
h.Write([]byte(id))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) {
|
||||
h, err := userIdSignatureHash(id, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// KeyIdString returns the public key's fingerprint in capital hex
|
||||
// (e.g. "6C7EE1B8621CC013").
|
||||
func (pk *PublicKey) KeyIdString() string {
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
|
||||
}
|
||||
|
||||
// KeyIdShortString returns the short form of public key's fingerprint
|
||||
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
||||
func (pk *PublicKey) KeyIdShortString() string {
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
|
||||
}
|
||||
|
||||
// BitLength returns the bit length for the given public key.
|
||||
func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
bitLength = pk.n.BitLength()
|
||||
case PubKeyAlgoDSA:
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoElGamal:
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoECDSA:
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoECDH:
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoEdDSA:
|
||||
bitLength = pk.p.BitLength()
|
||||
default:
|
||||
err = errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeyExpired returns whether sig is a self-signature of a key that has
|
||||
// expired or is created in the future.
|
||||
func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool {
|
||||
if pk.CreationTime.After(currentTime) {
|
||||
return true
|
||||
}
|
||||
if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 {
|
||||
return false
|
||||
}
|
||||
expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
|
||||
return currentTime.After(expiry)
|
||||
}
|
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go
generated
vendored
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.go
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
package packet
|
||||
|
||||
const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
|
||||
|
||||
const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
|
||||
|
||||
const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed"
|
||||
|
||||
const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0"
|
||||
|
||||
const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b"
|
||||
|
||||
const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4"
|
||||
|
||||
const ecdhFingerprintHex = "722354df2475a42164d1d49faa8b938f9a201946"
|
||||
|
||||
const ecdhPkDataHex = "b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec91803010909"
|
||||
|
||||
const eddsaFingerprintHex = "b2d5e5ec0e6deca6bc8eeeb00907e75e1dd99ad8"
|
||||
|
||||
const eddsaPkDataHex = "98330456e2132b16092b06010401da470f01010740bbda39266affa511a8c2d02edf690fb784b0499c4406185811a163539ef11dc1b41d74657374696e67203c74657374696e674074657374696e672e636f6d3e8879041316080021050256e2132b021b03050b09080702061508090a0b020416020301021e01021780000a09100907e75e1dd99ad86d0c00fe39d2008359352782bc9b61ac382584cd8eff3f57a18c2287e3afeeb05d1f04ba00fe2d0bc1ddf3ff8adb9afa3e7d9287244b4ec567f3db4d60b74a9b5465ed528203"
|
||||
|
||||
// Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key
|
||||
const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267`
|
78
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
78
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
|
||||
// that they result from the next call to Next.
|
||||
type Reader struct {
|
||||
q []Packet
|
||||
readers []io.Reader
|
||||
}
|
||||
|
||||
// New io.Readers are pushed when a compressed or encrypted packet is processed
|
||||
// and recursively treated as a new source of packets. However, a carefully
|
||||
// crafted packet can trigger an infinite recursive sequence of packets. See
|
||||
// http://mumble.net/~campbell/misc/pgp-quine
|
||||
// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402
|
||||
// This constant limits the number of recursive packets that may be pushed.
|
||||
const maxReaders = 32
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown packet types are skipped.
|
||||
func (r *Reader) Next() (p Packet, err error) {
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
|
||||
for len(r.readers) > 0 {
|
||||
p, err = Read(r.readers[len(r.readers)-1])
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
// TODO: Add strict mode that rejects unknown packets, instead of ignoring them.
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); !ok {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Push causes the Reader to start reading from a new io.Reader. When an EOF
|
||||
// error is seen from the new io.Reader, it is popped and the Reader continues
|
||||
// to read from the next most recent io.Reader. Push returns a StructuralError
|
||||
// if pushing the reader would exceed the maximum recursion level, otherwise it
|
||||
// returns nil.
|
||||
func (r *Reader) Push(reader io.Reader) (err error) {
|
||||
if len(r.readers) >= maxReaders {
|
||||
return errors.StructuralError("too many layers of packets")
|
||||
}
|
||||
r.readers = append(r.readers, reader)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unread causes the given Packet to be returned from the next call to Next.
|
||||
func (r *Reader) Unread(p Packet) {
|
||||
r.q = append(r.q, p)
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
q: nil,
|
||||
readers: []io.Reader{r},
|
||||
}
|
||||
}
|
983
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
983
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
|
@ -1,983 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
const (
|
||||
// See RFC 4880, section 5.2.3.21 for details.
|
||||
KeyFlagCertify = 1 << iota
|
||||
KeyFlagSign
|
||||
KeyFlagEncryptCommunications
|
||||
KeyFlagEncryptStorage
|
||||
)
|
||||
|
||||
// Signature represents a signature. See RFC 4880, section 5.2.
|
||||
type Signature struct {
|
||||
Version int
|
||||
SigType SignatureType
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
Hash crypto.Hash
|
||||
|
||||
// HashSuffix is extra data that is hashed in after the signed data.
|
||||
HashSuffix []byte
|
||||
// HashTag contains the first two bytes of the hash for fast rejection
|
||||
// of bad signed data.
|
||||
HashTag [2]byte
|
||||
|
||||
// Metadata includes format, filename and time, and is protected by v5
|
||||
// signatures of type 0x00 or 0x01. This metadata is included into the hash
|
||||
// computation; if nil, six 0x00 bytes are used instead. See section 5.2.4.
|
||||
Metadata *LiteralData
|
||||
|
||||
CreationTime time.Time
|
||||
|
||||
RSASignature encoding.Field
|
||||
DSASigR, DSASigS encoding.Field
|
||||
ECDSASigR, ECDSASigS encoding.Field
|
||||
EdDSASigR, EdDSASigS encoding.Field
|
||||
|
||||
// rawSubpackets contains the unparsed subpackets, in order.
|
||||
rawSubpackets []outputSubpacket
|
||||
|
||||
// The following are optional so are nil when not included in the
|
||||
// signature.
|
||||
|
||||
SigLifetimeSecs, KeyLifetimeSecs *uint32
|
||||
PreferredSymmetric, PreferredHash, PreferredCompression []uint8
|
||||
PreferredAEAD []uint8
|
||||
IssuerKeyId *uint64
|
||||
IssuerFingerprint []byte
|
||||
IsPrimaryId *bool
|
||||
|
||||
// PolicyURI can be set to the URI of a document that describes the
|
||||
// policy under which the signature was issued. See RFC 4880, section
|
||||
// 5.2.3.20 for details.
|
||||
PolicyURI string
|
||||
|
||||
// FlagsValid is set if any flags were given. See RFC 4880, section
|
||||
// 5.2.3.21 for details.
|
||||
FlagsValid bool
|
||||
FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool
|
||||
|
||||
// RevocationReason is set if this signature has been revoked.
|
||||
// See RFC 4880, section 5.2.3.23 for details.
|
||||
RevocationReason *uint8
|
||||
RevocationReasonText string
|
||||
|
||||
// In a self-signature, these flags are set there is a features subpacket
|
||||
// indicating that the issuer implementation supports these features
|
||||
// (section 5.2.5.25).
|
||||
MDC, AEAD, V5Keys bool
|
||||
|
||||
// EmbeddedSignature, if non-nil, is a signature of the parent key, by
|
||||
// this key. This prevents an attacker from claiming another's signing
|
||||
// subkey as their own.
|
||||
EmbeddedSignature *Signature
|
||||
|
||||
outSubpackets []outputSubpacket
|
||||
}
|
||||
|
||||
func (sig *Signature) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.2.3
|
||||
var buf [5]byte
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 && buf[0] != 5 {
|
||||
err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
return
|
||||
}
|
||||
sig.Version = int(buf[0])
|
||||
_, err = readFull(r, buf[:5])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sig.SigType = SignatureType(buf[0])
|
||||
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
|
||||
default:
|
||||
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||
return
|
||||
}
|
||||
|
||||
var ok bool
|
||||
sig.Hash, ok = s2k.HashIdToHash(buf[2])
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2])))
|
||||
}
|
||||
|
||||
hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4])
|
||||
hashedSubpackets := make([]byte, hashedSubpacketsLength)
|
||||
_, err = readFull(r, hashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sig.buildHashSuffix(hashedSubpackets)
|
||||
err = parseSignatureSubpackets(sig, hashedSubpackets, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = readFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1])
|
||||
unhashedSubpackets := make([]byte, unhashedSubpacketsLength)
|
||||
_, err = readFull(r, unhashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = parseSignatureSubpackets(sig, unhashedSubpackets, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = readFull(r, sig.HashTag[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
sig.RSASignature = new(encoding.MPI)
|
||||
_, err = sig.RSASignature.ReadFrom(r)
|
||||
case PubKeyAlgoDSA:
|
||||
sig.DSASigR = new(encoding.MPI)
|
||||
if _, err = sig.DSASigR.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sig.DSASigS = new(encoding.MPI)
|
||||
_, err = sig.DSASigS.ReadFrom(r)
|
||||
case PubKeyAlgoECDSA:
|
||||
sig.ECDSASigR = new(encoding.MPI)
|
||||
if _, err = sig.ECDSASigR.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sig.ECDSASigS = new(encoding.MPI)
|
||||
_, err = sig.ECDSASigS.ReadFrom(r)
|
||||
case PubKeyAlgoEdDSA:
|
||||
sig.EdDSASigR = new(encoding.MPI)
|
||||
if _, err = sig.EdDSASigR.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sig.EdDSASigS = new(encoding.MPI)
|
||||
if _, err = sig.EdDSASigS.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseSignatureSubpackets parses subpackets of the main signature packet. See
|
||||
// RFC 4880, section 5.2.3.1.
|
||||
func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) {
|
||||
for len(subpackets) > 0 {
|
||||
subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if sig.CreationTime.IsZero() {
|
||||
err = errors.StructuralError("no creation time in signature")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type signatureSubpacketType uint8
|
||||
|
||||
const (
|
||||
creationTimeSubpacket signatureSubpacketType = 2
|
||||
signatureExpirationSubpacket signatureSubpacketType = 3
|
||||
keyExpirationSubpacket signatureSubpacketType = 9
|
||||
prefSymmetricAlgosSubpacket signatureSubpacketType = 11
|
||||
issuerSubpacket signatureSubpacketType = 16
|
||||
prefHashAlgosSubpacket signatureSubpacketType = 21
|
||||
prefCompressionSubpacket signatureSubpacketType = 22
|
||||
primaryUserIdSubpacket signatureSubpacketType = 25
|
||||
policyUriSubpacket signatureSubpacketType = 26
|
||||
keyFlagsSubpacket signatureSubpacketType = 27
|
||||
reasonForRevocationSubpacket signatureSubpacketType = 29
|
||||
featuresSubpacket signatureSubpacketType = 30
|
||||
embeddedSignatureSubpacket signatureSubpacketType = 32
|
||||
issuerFingerprintSubpacket signatureSubpacketType = 33
|
||||
prefAeadAlgosSubpacket signatureSubpacketType = 34
|
||||
)
|
||||
|
||||
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
|
||||
func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) {
|
||||
// RFC 4880, section 5.2.3.1
|
||||
var (
|
||||
length uint32
|
||||
packetType signatureSubpacketType
|
||||
isCritical bool
|
||||
)
|
||||
switch {
|
||||
case subpacket[0] < 192:
|
||||
length = uint32(subpacket[0])
|
||||
subpacket = subpacket[1:]
|
||||
case subpacket[0] < 255:
|
||||
if len(subpacket) < 2 {
|
||||
goto Truncated
|
||||
}
|
||||
length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192
|
||||
subpacket = subpacket[2:]
|
||||
default:
|
||||
if len(subpacket) < 5 {
|
||||
goto Truncated
|
||||
}
|
||||
length = uint32(subpacket[1])<<24 |
|
||||
uint32(subpacket[2])<<16 |
|
||||
uint32(subpacket[3])<<8 |
|
||||
uint32(subpacket[4])
|
||||
subpacket = subpacket[5:]
|
||||
}
|
||||
if length > uint32(len(subpacket)) {
|
||||
goto Truncated
|
||||
}
|
||||
rest = subpacket[length:]
|
||||
subpacket = subpacket[:length]
|
||||
if len(subpacket) == 0 {
|
||||
err = errors.StructuralError("zero length signature subpacket")
|
||||
return
|
||||
}
|
||||
packetType = signatureSubpacketType(subpacket[0] & 0x7f)
|
||||
isCritical = subpacket[0]&0x80 == 0x80
|
||||
subpacket = subpacket[1:]
|
||||
sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket})
|
||||
switch packetType {
|
||||
case creationTimeSubpacket:
|
||||
if !isHashed {
|
||||
err = errors.StructuralError("signature creation time in non-hashed area")
|
||||
return
|
||||
}
|
||||
if len(subpacket) != 4 {
|
||||
err = errors.StructuralError("signature creation time not four bytes")
|
||||
return
|
||||
}
|
||||
t := binary.BigEndian.Uint32(subpacket)
|
||||
sig.CreationTime = time.Unix(int64(t), 0)
|
||||
case signatureExpirationSubpacket:
|
||||
// Signature expiration time, section 5.2.3.10
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) != 4 {
|
||||
err = errors.StructuralError("expiration subpacket with bad length")
|
||||
return
|
||||
}
|
||||
sig.SigLifetimeSecs = new(uint32)
|
||||
*sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket)
|
||||
case keyExpirationSubpacket:
|
||||
// Key expiration time, section 5.2.3.6
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) != 4 {
|
||||
err = errors.StructuralError("key expiration subpacket with bad length")
|
||||
return
|
||||
}
|
||||
sig.KeyLifetimeSecs = new(uint32)
|
||||
*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket)
|
||||
case prefSymmetricAlgosSubpacket:
|
||||
// Preferred symmetric algorithms, section 5.2.3.7
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
sig.PreferredSymmetric = make([]byte, len(subpacket))
|
||||
copy(sig.PreferredSymmetric, subpacket)
|
||||
case issuerSubpacket:
|
||||
if sig.Version > 4 {
|
||||
err = errors.StructuralError("issuer subpacket found in v5 key")
|
||||
}
|
||||
// Issuer, section 5.2.3.5
|
||||
if len(subpacket) != 8 {
|
||||
err = errors.StructuralError("issuer subpacket with bad length")
|
||||
return
|
||||
}
|
||||
sig.IssuerKeyId = new(uint64)
|
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
|
||||
case prefHashAlgosSubpacket:
|
||||
// Preferred hash algorithms, section 5.2.3.8
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
sig.PreferredHash = make([]byte, len(subpacket))
|
||||
copy(sig.PreferredHash, subpacket)
|
||||
case prefCompressionSubpacket:
|
||||
// Preferred compression algorithms, section 5.2.3.9
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
sig.PreferredCompression = make([]byte, len(subpacket))
|
||||
copy(sig.PreferredCompression, subpacket)
|
||||
case primaryUserIdSubpacket:
|
||||
// Primary User ID, section 5.2.3.19
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) != 1 {
|
||||
err = errors.StructuralError("primary user id subpacket with bad length")
|
||||
return
|
||||
}
|
||||
sig.IsPrimaryId = new(bool)
|
||||
if subpacket[0] > 0 {
|
||||
*sig.IsPrimaryId = true
|
||||
}
|
||||
case keyFlagsSubpacket:
|
||||
// Key flags, section 5.2.3.21
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) == 0 {
|
||||
err = errors.StructuralError("empty key flags subpacket")
|
||||
return
|
||||
}
|
||||
sig.FlagsValid = true
|
||||
if subpacket[0]&KeyFlagCertify != 0 {
|
||||
sig.FlagCertify = true
|
||||
}
|
||||
if subpacket[0]&KeyFlagSign != 0 {
|
||||
sig.FlagSign = true
|
||||
}
|
||||
if subpacket[0]&KeyFlagEncryptCommunications != 0 {
|
||||
sig.FlagEncryptCommunications = true
|
||||
}
|
||||
if subpacket[0]&KeyFlagEncryptStorage != 0 {
|
||||
sig.FlagEncryptStorage = true
|
||||
}
|
||||
case reasonForRevocationSubpacket:
|
||||
// Reason For Revocation, section 5.2.3.23
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) == 0 {
|
||||
err = errors.StructuralError("empty revocation reason subpacket")
|
||||
return
|
||||
}
|
||||
sig.RevocationReason = new(uint8)
|
||||
*sig.RevocationReason = subpacket[0]
|
||||
sig.RevocationReasonText = string(subpacket[1:])
|
||||
case featuresSubpacket:
|
||||
// Features subpacket, section 5.2.3.24 specifies a very general
|
||||
// mechanism for OpenPGP implementations to signal support for new
|
||||
// features.
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
if len(subpacket) > 0 {
|
||||
if subpacket[0]&0x01 != 0 {
|
||||
sig.MDC = true
|
||||
}
|
||||
if subpacket[0]&0x02 != 0 {
|
||||
sig.AEAD = true
|
||||
}
|
||||
if subpacket[0]&0x04 != 0 {
|
||||
sig.V5Keys = true
|
||||
}
|
||||
}
|
||||
case embeddedSignatureSubpacket:
|
||||
// Only usage is in signatures that cross-certify
|
||||
// signing subkeys. section 5.2.3.26 describes the
|
||||
// format, with its usage described in section 11.1
|
||||
if sig.EmbeddedSignature != nil {
|
||||
err = errors.StructuralError("Cannot have multiple embedded signatures")
|
||||
return
|
||||
}
|
||||
sig.EmbeddedSignature = new(Signature)
|
||||
// Embedded signatures are required to be v4 signatures see
|
||||
// section 12.1. However, we only parse v4 signatures in this
|
||||
// file anyway.
|
||||
if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding {
|
||||
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
|
||||
}
|
||||
case policyUriSubpacket:
|
||||
// Policy URI, section 5.2.3.20
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
sig.PolicyURI = string(subpacket)
|
||||
case issuerFingerprintSubpacket:
|
||||
v, l := subpacket[0], len(subpacket[1:])
|
||||
if v == 5 && l != 32 || v != 5 && l != 20 {
|
||||
return nil, errors.StructuralError("bad fingerprint length")
|
||||
}
|
||||
sig.IssuerFingerprint = make([]byte, l)
|
||||
copy(sig.IssuerFingerprint, subpacket[1:])
|
||||
sig.IssuerKeyId = new(uint64)
|
||||
if v == 5 {
|
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9])
|
||||
} else {
|
||||
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21])
|
||||
}
|
||||
case prefAeadAlgosSubpacket:
|
||||
// Preferred symmetric algorithms, section 5.2.3.8
|
||||
if !isHashed {
|
||||
return
|
||||
}
|
||||
sig.PreferredAEAD = make([]byte, len(subpacket))
|
||||
copy(sig.PreferredAEAD, subpacket)
|
||||
default:
|
||||
if isCritical {
|
||||
err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType)))
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
Truncated:
|
||||
err = errors.StructuralError("signature subpacket truncated")
|
||||
return
|
||||
}
|
||||
|
||||
// subpacketLengthLength returns the length, in bytes, of an encoded length value.
|
||||
func subpacketLengthLength(length int) int {
|
||||
if length < 192 {
|
||||
return 1
|
||||
}
|
||||
if length < 16320 {
|
||||
return 2
|
||||
}
|
||||
return 5
|
||||
}
|
||||
|
||||
func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool {
|
||||
if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 {
|
||||
return bytes.Equal(sig.IssuerFingerprint, pk.Fingerprint)
|
||||
}
|
||||
return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId
|
||||
}
|
||||
|
||||
// serializeSubpacketLength marshals the given length into to.
|
||||
func serializeSubpacketLength(to []byte, length int) int {
|
||||
// RFC 4880, Section 4.2.2.
|
||||
if length < 192 {
|
||||
to[0] = byte(length)
|
||||
return 1
|
||||
}
|
||||
if length < 16320 {
|
||||
length -= 192
|
||||
to[0] = byte((length >> 8) + 192)
|
||||
to[1] = byte(length)
|
||||
return 2
|
||||
}
|
||||
to[0] = 255
|
||||
to[1] = byte(length >> 24)
|
||||
to[2] = byte(length >> 16)
|
||||
to[3] = byte(length >> 8)
|
||||
to[4] = byte(length)
|
||||
return 5
|
||||
}
|
||||
|
||||
// subpacketsLength returns the serialized length, in bytes, of the given
|
||||
// subpackets.
|
||||
func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) {
|
||||
for _, subpacket := range subpackets {
|
||||
if subpacket.hashed == hashed {
|
||||
length += subpacketLengthLength(len(subpacket.contents) + 1)
|
||||
length += 1 // type byte
|
||||
length += len(subpacket.contents)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// serializeSubpackets marshals the given subpackets into to.
|
||||
func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
|
||||
for _, subpacket := range subpackets {
|
||||
if subpacket.hashed == hashed {
|
||||
n := serializeSubpacketLength(to, len(subpacket.contents)+1)
|
||||
to[n] = byte(subpacket.subpacketType)
|
||||
if subpacket.isCritical {
|
||||
to[n] |= 0x80
|
||||
}
|
||||
to = to[1+n:]
|
||||
n = copy(to, subpacket.contents)
|
||||
to = to[n:]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SigExpired returns whether sig is a signature that has expired or is created
|
||||
// in the future.
|
||||
func (sig *Signature) SigExpired(currentTime time.Time) bool {
|
||||
if sig.CreationTime.After(currentTime) {
|
||||
return true
|
||||
}
|
||||
if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 {
|
||||
return false
|
||||
}
|
||||
expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second)
|
||||
return currentTime.After(expiry)
|
||||
}
|
||||
|
||||
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
|
||||
func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) {
|
||||
hash, ok := s2k.HashToHashId(sig.Hash)
|
||||
if !ok {
|
||||
sig.HashSuffix = nil
|
||||
return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash)))
|
||||
}
|
||||
|
||||
hashedFields := bytes.NewBuffer([]byte{
|
||||
uint8(sig.Version),
|
||||
uint8(sig.SigType),
|
||||
uint8(sig.PubKeyAlgo),
|
||||
uint8(hash),
|
||||
uint8(len(hashedSubpackets) >> 8),
|
||||
uint8(len(hashedSubpackets)),
|
||||
})
|
||||
hashedFields.Write(hashedSubpackets)
|
||||
|
||||
var l uint64 = uint64(6 + len(hashedSubpackets))
|
||||
if sig.Version == 5 {
|
||||
hashedFields.Write([]byte{0x05, 0xff})
|
||||
hashedFields.Write([]byte{
|
||||
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32),
|
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
|
||||
})
|
||||
} else {
|
||||
hashedFields.Write([]byte{0x04, 0xff})
|
||||
hashedFields.Write([]byte{
|
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
|
||||
})
|
||||
}
|
||||
sig.HashSuffix = make([]byte, hashedFields.Len())
|
||||
copy(sig.HashSuffix, hashedFields.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
|
||||
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)
|
||||
hashedSubpackets := make([]byte, hashedSubpacketsLen)
|
||||
serializeSubpackets(hashedSubpackets, sig.outSubpackets, true)
|
||||
err = sig.buildHashSuffix(hashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
|
||||
sig.AddMetadataToHashSuffix()
|
||||
}
|
||||
|
||||
h.Write(sig.HashSuffix)
|
||||
digest = h.Sum(nil)
|
||||
copy(sig.HashTag[:], digest)
|
||||
return
|
||||
}
|
||||
|
||||
// Sign signs a message with a private key. The hash, h, must contain
|
||||
// the hash of the message to be signed and will be mutated by this function.
|
||||
// On success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) {
|
||||
if priv.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
sig.Version = priv.PublicKey.Version
|
||||
sig.IssuerFingerprint = priv.PublicKey.Fingerprint
|
||||
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
digest, err := sig.signPrepareHash(h)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
// supports both *rsa.PrivateKey and crypto.Signer
|
||||
sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
|
||||
if err == nil {
|
||||
sig.RSASignature = encoding.NewMPI(sigdata)
|
||||
}
|
||||
case PubKeyAlgoDSA:
|
||||
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey)
|
||||
|
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8
|
||||
if len(digest) > subgroupSize {
|
||||
digest = digest[:subgroupSize]
|
||||
}
|
||||
r, s, err := dsa.Sign(config.Random(), dsaPriv, digest)
|
||||
if err == nil {
|
||||
sig.DSASigR = new(encoding.MPI).SetBig(r)
|
||||
sig.DSASigS = new(encoding.MPI).SetBig(s)
|
||||
}
|
||||
case PubKeyAlgoECDSA:
|
||||
var r, s *big.Int
|
||||
if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok {
|
||||
// direct support, avoid asn1 wrapping/unwrapping
|
||||
r, s, err = ecdsa.Sign(config.Random(), pk, digest)
|
||||
} else {
|
||||
var b []byte
|
||||
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
|
||||
if err == nil {
|
||||
r, s, err = unwrapECDSASig(b)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
sig.ECDSASigR = new(encoding.MPI).SetBig(r)
|
||||
sig.ECDSASigS = new(encoding.MPI).SetBig(s)
|
||||
}
|
||||
case PubKeyAlgoEdDSA:
|
||||
sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, crypto.Hash(0))
|
||||
if err == nil {
|
||||
sig.EdDSASigR = encoding.NewMPI(sigdata[:32])
|
||||
sig.EdDSASigS = encoding.NewMPI(sigdata[32:])
|
||||
}
|
||||
default:
|
||||
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA
|
||||
// signature.
|
||||
func unwrapECDSASig(b []byte) (r, s *big.Int, err error) {
|
||||
var ecsdaSig struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
_, err = asn1.Unmarshal(b, &ecsdaSig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return ecsdaSig.R, ecsdaSig.S, nil
|
||||
}
|
||||
|
||||
// SignUserId computes a signature from priv, asserting that pub is a valid
|
||||
// key for the identity id. On success, the signature is stored in sig. Call
|
||||
// Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error {
|
||||
if priv.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
h, err := userIdSignatureHash(id, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.Sign(h, priv, config)
|
||||
}
|
||||
|
||||
// CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success,
|
||||
// the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) CrossSignKey(pub *PublicKey, hashKey *PublicKey, signingKey *PrivateKey,
|
||||
config *Config) error {
|
||||
h, err := keySignatureHash(hashKey, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.Sign(h, signingKey, config)
|
||||
}
|
||||
|
||||
// SignKey computes a signature from priv, asserting that pub is a subkey. On
|
||||
// success, the signature is stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
|
||||
if priv.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.Sign(h, priv, config)
|
||||
}
|
||||
|
||||
// RevokeKey computes a revocation signature of pub using priv. On success, the signature is
|
||||
// stored in sig. Call Serialize to write it out.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (sig *Signature) RevokeKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
|
||||
h, err := keyRevocationHash(pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.Sign(h, priv, config)
|
||||
}
|
||||
|
||||
// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
|
||||
// called first.
|
||||
func (sig *Signature) Serialize(w io.Writer) (err error) {
|
||||
if len(sig.outSubpackets) == 0 {
|
||||
sig.outSubpackets = sig.rawSubpackets
|
||||
}
|
||||
if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil {
|
||||
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
|
||||
}
|
||||
|
||||
sigLength := 0
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
sigLength = int(sig.RSASignature.EncodedLength())
|
||||
case PubKeyAlgoDSA:
|
||||
sigLength = int(sig.DSASigR.EncodedLength())
|
||||
sigLength += int(sig.DSASigS.EncodedLength())
|
||||
case PubKeyAlgoECDSA:
|
||||
sigLength = int(sig.ECDSASigR.EncodedLength())
|
||||
sigLength += int(sig.ECDSASigS.EncodedLength())
|
||||
case PubKeyAlgoEdDSA:
|
||||
sigLength = int(sig.EdDSASigR.EncodedLength())
|
||||
sigLength += int(sig.EdDSASigS.EncodedLength())
|
||||
default:
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
|
||||
length := len(sig.HashSuffix) - 6 /* trailer not included */ +
|
||||
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
|
||||
2 /* hash tag */ + sigLength
|
||||
if sig.Version == 5 {
|
||||
length -= 4 // eight-octet instead of four-octet big endian
|
||||
}
|
||||
err = serializeHeader(w, packetTypeSignature, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = sig.serializeBody(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sig *Signature) serializeBody(w io.Writer) (err error) {
|
||||
hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | uint16(sig.HashSuffix[5])
|
||||
fields := sig.HashSuffix[:6+hashedSubpacketsLen]
|
||||
_, err = w.Write(fields)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
|
||||
unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen)
|
||||
unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
|
||||
unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
|
||||
serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)
|
||||
|
||||
_, err = w.Write(unhashedSubpackets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(sig.HashTag[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch sig.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
_, err = w.Write(sig.RSASignature.EncodedBytes())
|
||||
case PubKeyAlgoDSA:
|
||||
if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(sig.DSASigS.EncodedBytes())
|
||||
case PubKeyAlgoECDSA:
|
||||
if _, err = w.Write(sig.ECDSASigR.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(sig.ECDSASigS.EncodedBytes())
|
||||
case PubKeyAlgoEdDSA:
|
||||
if _, err = w.Write(sig.EdDSASigR.EncodedBytes()); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(sig.EdDSASigS.EncodedBytes())
|
||||
default:
|
||||
panic("impossible")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// outputSubpacket represents a subpacket to be marshaled.
|
||||
type outputSubpacket struct {
|
||||
hashed bool // true if this subpacket is in the hashed area.
|
||||
subpacketType signatureSubpacketType
|
||||
isCritical bool
|
||||
contents []byte
|
||||
}
|
||||
|
||||
func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) {
|
||||
creationTime := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
|
||||
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
|
||||
|
||||
if sig.IssuerKeyId != nil && sig.Version == 4 {
|
||||
keyId := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
|
||||
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId})
|
||||
}
|
||||
if sig.IssuerFingerprint != nil {
|
||||
contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...)
|
||||
subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, true, contents})
|
||||
}
|
||||
if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
|
||||
sigLifetime := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs)
|
||||
subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime})
|
||||
}
|
||||
|
||||
// Key flags may only appear in self-signatures or certification signatures.
|
||||
|
||||
if sig.FlagsValid {
|
||||
var flags byte
|
||||
if sig.FlagCertify {
|
||||
flags |= KeyFlagCertify
|
||||
}
|
||||
if sig.FlagSign {
|
||||
flags |= KeyFlagSign
|
||||
}
|
||||
if sig.FlagEncryptCommunications {
|
||||
flags |= KeyFlagEncryptCommunications
|
||||
}
|
||||
if sig.FlagEncryptStorage {
|
||||
flags |= KeyFlagEncryptStorage
|
||||
}
|
||||
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}})
|
||||
}
|
||||
|
||||
// The following subpackets may only appear in self-signatures.
|
||||
|
||||
var features = byte(0x00)
|
||||
if sig.MDC {
|
||||
features |= 0x01
|
||||
}
|
||||
if sig.AEAD {
|
||||
features |= 0x02
|
||||
}
|
||||
if sig.V5Keys {
|
||||
features |= 0x04
|
||||
}
|
||||
|
||||
if features != 0x00 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}})
|
||||
}
|
||||
|
||||
if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 {
|
||||
keyLifetime := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs)
|
||||
subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime})
|
||||
}
|
||||
|
||||
if sig.IsPrimaryId != nil && *sig.IsPrimaryId {
|
||||
subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}})
|
||||
}
|
||||
|
||||
if len(sig.PreferredSymmetric) > 0 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric})
|
||||
}
|
||||
|
||||
if len(sig.PreferredHash) > 0 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash})
|
||||
}
|
||||
|
||||
if len(sig.PreferredCompression) > 0 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
|
||||
}
|
||||
|
||||
if len(sig.PolicyURI) > 0 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)})
|
||||
}
|
||||
|
||||
if len(sig.PreferredAEAD) > 0 {
|
||||
subpackets = append(subpackets, outputSubpacket{true, prefAeadAlgosSubpacket, false, sig.PreferredAEAD})
|
||||
}
|
||||
|
||||
// Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23.
|
||||
if sig.RevocationReason != nil {
|
||||
subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true,
|
||||
append([]uint8{*sig.RevocationReason}, []uint8(sig.RevocationReasonText)...)})
|
||||
}
|
||||
|
||||
// EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26.
|
||||
if sig.EmbeddedSignature != nil {
|
||||
var buf bytes.Buffer
|
||||
err = sig.EmbeddedSignature.serializeBody(&buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddMetadataToHashSuffix modifies the current hash suffix to include metadata
|
||||
// (format, filename, and time). Version 5 keys protect this data including it
|
||||
// in the hash computation. See section 5.2.4.
|
||||
func (sig *Signature) AddMetadataToHashSuffix() {
|
||||
if sig == nil || sig.Version != 5 {
|
||||
return
|
||||
}
|
||||
if sig.SigType != 0x00 && sig.SigType != 0x01 {
|
||||
return
|
||||
}
|
||||
lit := sig.Metadata
|
||||
if lit == nil {
|
||||
// This will translate into six 0x00 bytes.
|
||||
lit = &LiteralData{}
|
||||
}
|
||||
|
||||
// Extract the current byte count
|
||||
n := sig.HashSuffix[len(sig.HashSuffix)-8:]
|
||||
l := uint64(
|
||||
uint64(n[0])<<56 | uint64(n[1])<<48 | uint64(n[2])<<40 | uint64(n[3])<<32 |
|
||||
uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7]))
|
||||
|
||||
suffix := bytes.NewBuffer(nil)
|
||||
suffix.Write(sig.HashSuffix[:l])
|
||||
|
||||
// Add the metadata
|
||||
var buf [4]byte
|
||||
buf[0] = lit.Format
|
||||
fileName := lit.FileName
|
||||
if len(lit.FileName) > 255 {
|
||||
fileName = fileName[:255]
|
||||
}
|
||||
buf[1] = byte(len(fileName))
|
||||
suffix.Write(buf[:2])
|
||||
suffix.Write([]byte(lit.FileName))
|
||||
binary.BigEndian.PutUint32(buf[:], lit.Time)
|
||||
suffix.Write(buf[:])
|
||||
|
||||
// Update the counter and restore trailing bytes
|
||||
l = uint64(suffix.Len())
|
||||
suffix.Write([]byte{0x05, 0xff})
|
||||
suffix.Write([]byte{
|
||||
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32),
|
||||
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
|
||||
})
|
||||
sig.HashSuffix = suffix.Bytes()
|
||||
}
|
267
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
267
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
|
@ -1,267 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// This is the largest session key that we'll support. Since no 512-bit cipher
|
||||
// has even been seriously used, this is comfortably large.
|
||||
const maxSessionKeySizeInBytes = 64
|
||||
|
||||
// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
|
||||
// 4880, section 5.3.
|
||||
type SymmetricKeyEncrypted struct {
|
||||
Version int
|
||||
CipherFunc CipherFunction
|
||||
Mode AEADMode
|
||||
s2k func(out, in []byte)
|
||||
aeadNonce []byte
|
||||
encryptedKey []byte
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
// RFC 4880, section 5.3.
|
||||
var buf [2]byte
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
ske.Version = int(buf[0])
|
||||
if ske.Version != 4 && ske.Version != 5 {
|
||||
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
}
|
||||
ske.CipherFunc = CipherFunction(buf[1])
|
||||
if ske.CipherFunc.KeySize() == 0 {
|
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
|
||||
}
|
||||
|
||||
if ske.Version == 5 {
|
||||
mode := make([]byte, 1)
|
||||
if _, err := r.Read(mode); err != nil {
|
||||
return errors.StructuralError("cannot read AEAD octect from packet")
|
||||
}
|
||||
ske.Mode = AEADMode(mode[0])
|
||||
}
|
||||
|
||||
var err error
|
||||
if ske.s2k, err = s2k.Parse(r); err != nil {
|
||||
if _, ok := err.(errors.ErrDummyPrivateKey); ok {
|
||||
return errors.UnsupportedError("missing key GNU extension in session key")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if ske.Version == 5 {
|
||||
// AEAD nonce
|
||||
nonce := make([]byte, ske.Mode.NonceLength())
|
||||
_, err := readFull(r, nonce)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
ske.aeadNonce = nonce
|
||||
}
|
||||
|
||||
encryptedKey := make([]byte, maxSessionKeySizeInBytes)
|
||||
// The session key may follow. We just have to try and read to find
|
||||
// out. If it exists then we limit it to maxSessionKeySizeInBytes.
|
||||
n, err := readFull(r, encryptedKey)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if n != 0 {
|
||||
if n == maxSessionKeySizeInBytes {
|
||||
return errors.UnsupportedError("oversized encrypted session key")
|
||||
}
|
||||
ske.encryptedKey = encryptedKey[:n]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt attempts to decrypt an encrypted session key and returns the key and
|
||||
// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
|
||||
// packet.
|
||||
func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
|
||||
key := make([]byte, ske.CipherFunc.KeySize())
|
||||
ske.s2k(key, passphrase)
|
||||
if len(ske.encryptedKey) == 0 {
|
||||
return key, ske.CipherFunc, nil
|
||||
}
|
||||
switch ske.Version {
|
||||
case 4:
|
||||
plaintextKey, cipherFunc, err := ske.decryptV4(key)
|
||||
return plaintextKey, cipherFunc, err
|
||||
case 5:
|
||||
plaintextKey, err := ske.decryptV5(key)
|
||||
return plaintextKey, CipherFunction(0), err
|
||||
}
|
||||
err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
return nil, CipherFunction(0), err
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, error) {
|
||||
// the IV is all zeros
|
||||
iv := make([]byte, ske.CipherFunc.blockSize())
|
||||
c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
|
||||
plaintextKey := make([]byte, len(ske.encryptedKey))
|
||||
c.XORKeyStream(plaintextKey, ske.encryptedKey)
|
||||
cipherFunc := CipherFunction(plaintextKey[0])
|
||||
if cipherFunc.blockSize() == 0 {
|
||||
return nil, ske.CipherFunc, errors.UnsupportedError(
|
||||
"unknown cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
plaintextKey = plaintextKey[1:]
|
||||
if len(plaintextKey) != cipherFunc.KeySize() {
|
||||
return nil, cipherFunc, errors.StructuralError(
|
||||
"length of decrypted key not equal to cipher keysize")
|
||||
}
|
||||
return plaintextKey, cipherFunc, nil
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) {
|
||||
blockCipher := CipherFunction(ske.CipherFunc).new(key)
|
||||
aead := ske.Mode.new(blockCipher)
|
||||
|
||||
adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)}
|
||||
plaintextKey, err := aead.Open(nil, ske.aeadNonce, ske.encryptedKey, adata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintextKey, nil
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w.
|
||||
// The packet contains a random session key, encrypted by a key derived from
|
||||
// the given passphrase. The session key is returned and must be passed to
|
||||
// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on
|
||||
// whether config.AEADConfig != nil.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
|
||||
cipherFunc := config.Cipher()
|
||||
keySize := cipherFunc.KeySize()
|
||||
if keySize == 0 {
|
||||
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
|
||||
sessionKey := make([]byte, keySize)
|
||||
_, err = io.ReadFull(config.Random(), sessionKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = SerializeSymmetricKeyEncryptedReuseKey(w, sessionKey, passphrase, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
key = sessionKey
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncryptedReuseKey serializes a symmetric key packet to w.
|
||||
// The packet contains the given session key, encrypted by a key derived from
|
||||
// the given passphrase. The session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on
|
||||
// whether config.AEADConfig != nil.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
|
||||
var version int
|
||||
if config.AEAD() != nil {
|
||||
version = 5
|
||||
} else {
|
||||
version = 4
|
||||
}
|
||||
cipherFunc := config.Cipher()
|
||||
keySize := cipherFunc.KeySize()
|
||||
if keySize == 0 {
|
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
|
||||
s2kBuf := new(bytes.Buffer)
|
||||
keyEncryptingKey := make([]byte, keySize)
|
||||
// s2k.Serialize salts and stretches the passphrase, and writes the
|
||||
// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
|
||||
err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2kBytes := s2kBuf.Bytes()
|
||||
|
||||
var packetLength int
|
||||
switch version {
|
||||
case 4:
|
||||
packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
|
||||
case 5:
|
||||
nonceLen := config.AEAD().Mode().NonceLength()
|
||||
tagLen := config.AEAD().Mode().TagLength()
|
||||
packetLength = 3 + len(s2kBytes) + nonceLen + keySize + tagLen
|
||||
}
|
||||
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 2)
|
||||
// Symmetric Key Encrypted Version
|
||||
buf[0] = byte(version)
|
||||
// Cipher function
|
||||
buf[1] = byte(cipherFunc)
|
||||
|
||||
if version == 5 {
|
||||
// AEAD mode
|
||||
buf = append(buf, byte(config.AEAD().Mode()))
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(s2kBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch version {
|
||||
case 4:
|
||||
iv := make([]byte, cipherFunc.blockSize())
|
||||
c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
|
||||
encryptedCipherAndKey := make([]byte, keySize+1)
|
||||
c.XORKeyStream(encryptedCipherAndKey, buf[1:])
|
||||
c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
|
||||
_, err = w.Write(encryptedCipherAndKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case 5:
|
||||
blockCipher := cipherFunc.new(keyEncryptingKey)
|
||||
mode := config.AEAD().Mode()
|
||||
aead := mode.new(blockCipher)
|
||||
// Sample nonce using random reader
|
||||
nonce := make([]byte, config.AEAD().Mode().NonceLength())
|
||||
_, err = io.ReadFull(config.Random(), nonce)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Seal and write (encryptedData includes auth. tag)
|
||||
adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)}
|
||||
encryptedData := aead.Seal(nil, nonce, sessionKey, adata)
|
||||
_, err = w.Write(nonce)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(encryptedData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
290
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
290
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
|
@ -1,290 +0,0 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/sha1"
|
||||
"crypto/subtle"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
|
||||
// encrypted Contents will consist of more OpenPGP packets. See RFC 4880,
|
||||
// sections 5.7 and 5.13.
|
||||
type SymmetricallyEncrypted struct {
|
||||
MDC bool // true iff this is a type 18 packet and thus has an embedded MAC.
|
||||
Contents io.Reader
|
||||
prefix []byte
|
||||
}
|
||||
|
||||
const symmetricallyEncryptedVersion = 1
|
||||
|
||||
func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
|
||||
if se.MDC {
|
||||
// See RFC 4880, section 5.13.
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if buf[0] != symmetricallyEncryptedVersion {
|
||||
return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
|
||||
}
|
||||
}
|
||||
se.Contents = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt returns a ReadCloser, from which the decrypted Contents of the
|
||||
// packet can be read. An incorrect key will only be detected after trying
|
||||
// to decrypt the entire data.
|
||||
func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) {
|
||||
keySize := c.KeySize()
|
||||
if keySize == 0 {
|
||||
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
|
||||
}
|
||||
if len(key) != keySize {
|
||||
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
|
||||
}
|
||||
|
||||
if se.prefix == nil {
|
||||
se.prefix = make([]byte, c.blockSize()+2)
|
||||
_, err := readFull(se.Contents, se.prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if len(se.prefix) != c.blockSize()+2 {
|
||||
return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths")
|
||||
}
|
||||
|
||||
ocfbResync := OCFBResync
|
||||
if se.MDC {
|
||||
// MDC packets use a different form of OCFB mode.
|
||||
ocfbResync = OCFBNoResync
|
||||
}
|
||||
|
||||
s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
|
||||
|
||||
plaintext := cipher.StreamReader{S: s, R: se.Contents}
|
||||
|
||||
if se.MDC {
|
||||
// MDC packets have an embedded hash that we need to check.
|
||||
h := sha1.New()
|
||||
h.Write(se.prefix)
|
||||
return &seMDCReader{in: plaintext, h: h}, nil
|
||||
}
|
||||
|
||||
// Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser.
|
||||
return seReader{plaintext}, nil
|
||||
}
|
||||
|
||||
// seReader wraps an io.Reader with a no-op Close method.
|
||||
type seReader struct {
|
||||
in io.Reader
|
||||
}
|
||||
|
||||
func (ser seReader) Read(buf []byte) (int, error) {
|
||||
return ser.in.Read(buf)
|
||||
}
|
||||
|
||||
func (ser seReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
|
||||
|
||||
// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold
|
||||
// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an
|
||||
// MDC packet containing a hash of the previous Contents which is checked
|
||||
// against the running hash. See RFC 4880, section 5.13.
|
||||
type seMDCReader struct {
|
||||
in io.Reader
|
||||
h hash.Hash
|
||||
trailer [mdcTrailerSize]byte
|
||||
scratch [mdcTrailerSize]byte
|
||||
trailerUsed int
|
||||
error bool
|
||||
eof bool
|
||||
}
|
||||
|
||||
func (ser *seMDCReader) Read(buf []byte) (n int, err error) {
|
||||
if ser.error {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
if ser.eof {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
|
||||
// If we haven't yet filled the trailer buffer then we must do that
|
||||
// first.
|
||||
for ser.trailerUsed < mdcTrailerSize {
|
||||
n, err = ser.in.Read(ser.trailer[ser.trailerUsed:])
|
||||
ser.trailerUsed += n
|
||||
if err == io.EOF {
|
||||
if ser.trailerUsed != mdcTrailerSize {
|
||||
n = 0
|
||||
err = io.ErrUnexpectedEOF
|
||||
ser.error = true
|
||||
return
|
||||
}
|
||||
ser.eof = true
|
||||
n = 0
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a short read then we read into a temporary buffer and shift
|
||||
// the data into the caller's buffer.
|
||||
if len(buf) <= mdcTrailerSize {
|
||||
n, err = readFull(ser.in, ser.scratch[:len(buf)])
|
||||
copy(buf, ser.trailer[:n])
|
||||
ser.h.Write(buf[:n])
|
||||
copy(ser.trailer[:], ser.trailer[n:])
|
||||
copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:])
|
||||
if n < len(buf) {
|
||||
ser.eof = true
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n, err = ser.in.Read(buf[mdcTrailerSize:])
|
||||
copy(buf, ser.trailer[:])
|
||||
ser.h.Write(buf[:n])
|
||||
copy(ser.trailer[:], buf[n:])
|
||||
|
||||
if err == io.EOF {
|
||||
ser.eof = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// This is a new-format packet tag byte for a type 19 (MDC) packet.
|
||||
const mdcPacketTagByte = byte(0x80) | 0x40 | 19
|
||||
|
||||
func (ser *seMDCReader) Close() error {
|
||||
if ser.error {
|
||||
return errors.ErrMDCMissing
|
||||
}
|
||||
|
||||
for !ser.eof {
|
||||
// We haven't seen EOF so we need to read to the end
|
||||
var buf [1024]byte
|
||||
_, err := ser.Read(buf[:])
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return errors.ErrMDCMissing
|
||||
}
|
||||
}
|
||||
|
||||
ser.h.Write(ser.trailer[:2])
|
||||
|
||||
final := ser.h.Sum(nil)
|
||||
if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
// The hash already includes the MDC header, but we still check its value
|
||||
// to confirm encryption correctness
|
||||
if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
|
||||
return errors.ErrMDCMissing
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// An seMDCWriter writes through to an io.WriteCloser while maintains a running
|
||||
// hash of the data written. On close, it emits an MDC packet containing the
|
||||
// running hash.
|
||||
type seMDCWriter struct {
|
||||
w io.WriteCloser
|
||||
h hash.Hash
|
||||
}
|
||||
|
||||
func (w *seMDCWriter) Write(buf []byte) (n int, err error) {
|
||||
w.h.Write(buf)
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
|
||||
func (w *seMDCWriter) Close() (err error) {
|
||||
var buf [mdcTrailerSize]byte
|
||||
|
||||
buf[0] = mdcPacketTagByte
|
||||
buf[1] = sha1.Size
|
||||
w.h.Write(buf[:2])
|
||||
digest := w.h.Sum(nil)
|
||||
copy(buf[2:], digest)
|
||||
|
||||
_, err = w.w.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return w.w.Close()
|
||||
}
|
||||
|
||||
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
|
||||
type noOpCloser struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (c noOpCloser) Write(data []byte) (n int, err error) {
|
||||
return c.w.Write(data)
|
||||
}
|
||||
|
||||
func (c noOpCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
|
||||
// to w and returns a WriteCloser to which the to-be-encrypted packets can be
|
||||
// written.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) {
|
||||
if c.KeySize() != len(key) {
|
||||
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
|
||||
}
|
||||
writeCloser := noOpCloser{w}
|
||||
ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
block := c.new(key)
|
||||
blockSize := block.BlockSize()
|
||||
iv := make([]byte, blockSize)
|
||||
_, err = config.Random().Read(iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
|
||||
_, err = ciphertext.Write(prefix)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
plaintext := cipher.StreamWriter{S: s, W: ciphertext}
|
||||
|
||||
h := sha1.New()
|
||||
h.Write(iv)
|
||||
h.Write(iv[blockSize-2:])
|
||||
Contents = &seMDCWriter{w: plaintext, h: h}
|
||||
return
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user