Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe13e8dff6 | |||
10d070dd10 | |||
af826ff457 | |||
90bfc25975 | |||
dadb907740 | |||
bfb5ee44ce | |||
87eac3e11b | |||
60cc3f4073 | |||
d952775922 | |||
2c822aeade | |||
aea5c7619f | |||
dc90ed4488 | |||
b20b1719bd |
159
.drone.yml
159
.drone.yml
@ -1,89 +1,70 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: cleanup-before
|
||||
name: build-linux
|
||||
|
||||
environment:
|
||||
GOOS: linux
|
||||
GOOPTIONS: -mod=vendor
|
||||
SRCFILES: cmd/pki/*.go
|
||||
PROJECTNAME: pki
|
||||
|
||||
steps:
|
||||
- name: clean
|
||||
image: alpine
|
||||
commands:
|
||||
- rm -rf /build/*
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
when:
|
||||
event: tag
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /tmp/pki/build
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default-linux-amd64
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
- name: build-linux-amd64
|
||||
image: golang
|
||||
commands:
|
||||
- ./ci-build.sh build
|
||||
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
|
||||
environment:
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /tmp/pki/build
|
||||
|
||||
depends_on:
|
||||
- cleanup-before
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default-linux-arm64
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- tag
|
||||
- name: build-linux-arm64
|
||||
image: golang
|
||||
commands:
|
||||
- ./ci-build.sh build
|
||||
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
|
||||
environment:
|
||||
GOOS: linux
|
||||
GOARCH: arm64
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /tmp/pki/build
|
||||
|
||||
depends_on:
|
||||
- cleanup-before
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- tag
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: gitea-release
|
||||
name: gitea-release-linux
|
||||
|
||||
environment:
|
||||
GOOS: linux
|
||||
GOOPTIONS: -mod=vendor
|
||||
SRCFILES: cmd/pki/*.go
|
||||
PROJECTNAME: pki
|
||||
|
||||
steps:
|
||||
- name: move
|
||||
image: alpine
|
||||
- name: build-linux-amd64
|
||||
image: golang
|
||||
commands:
|
||||
- mv build/* ./
|
||||
volumes:
|
||||
- name: build
|
||||
path: /drone/src/build
|
||||
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
|
||||
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
|
||||
- echo $PROJECTNAME $DRONE_TAG > VERSION
|
||||
environment:
|
||||
GOARCH: amd64
|
||||
when:
|
||||
event: tag
|
||||
event:
|
||||
- tag
|
||||
- name: build-linux-arm64
|
||||
image: golang
|
||||
commands:
|
||||
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
|
||||
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
|
||||
- echo $PROJECTNAME $DRONE_TAG > VERSION
|
||||
environment:
|
||||
GOARCH: arm64
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
- name: release
|
||||
image: plugins/gitea-release
|
||||
settings:
|
||||
@ -95,50 +76,6 @@ steps:
|
||||
- sha256
|
||||
- sha512
|
||||
title: VERSION
|
||||
volumes:
|
||||
- name: build
|
||||
path: /drone/src/build
|
||||
when:
|
||||
event: tag
|
||||
- name: ls
|
||||
image: alpine
|
||||
commands:
|
||||
- find .
|
||||
volumes:
|
||||
- name: build
|
||||
path: /drone/src/build
|
||||
when:
|
||||
event: tag
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /tmp/pki/build
|
||||
|
||||
depends_on:
|
||||
- default-linux-amd64
|
||||
- default-linux-arm64
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: cleanup-after
|
||||
|
||||
steps:
|
||||
- name: clean
|
||||
image: alpine
|
||||
commands:
|
||||
- rm -rf /build/*
|
||||
volumes:
|
||||
- name: build
|
||||
path: /build
|
||||
when:
|
||||
event: tag
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /tmp/pki/build
|
||||
|
||||
depends_on:
|
||||
- gitea-release
|
||||
event:
|
||||
- tag
|
||||
|
18
Makefile
18
Makefile
@ -1,18 +0,0 @@
|
||||
# pki Makefile
|
||||
|
||||
GOCMD=go
|
||||
GOBUILDCMD=${GOCMD} build
|
||||
GOOPTIONS=-mod=vendor -ldflags="-s -w"
|
||||
|
||||
RMCMD=rm
|
||||
BINNAME=pki
|
||||
|
||||
SRCFILES=cmd/pki/*.go
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
${GOBUILDCMD} ${GOOPTIONS} ${SRCFILES}
|
||||
|
||||
clean:
|
||||
${RMCMD} -f ${BINNAME}
|
@ -10,7 +10,7 @@ PKI is a centralized Letsencrypt database server and renewer for certificate man
|
||||
### Build
|
||||
|
||||
```bash
|
||||
make
|
||||
go build cmd/pki/pki.go
|
||||
```
|
||||
|
||||
### Sample config in pki.ini
|
||||
@ -40,7 +40,7 @@ ovhck=
|
||||
## License
|
||||
|
||||
```text
|
||||
Copyright (c) 2020, 2021 PaulBSD
|
||||
Copyright (c) 2020, 2021, 2022 PaulBSD
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
62
ci-build.sh
62
ci-build.sh
@ -1,62 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PROJECTNAME=pki
|
||||
RELEASENAME=${PROJECTNAME}
|
||||
VERSION="0"
|
||||
|
||||
GOOPTIONS="-mod=vendor"
|
||||
SRCFILES=cmd/${PROJECTNAME}/*.go
|
||||
|
||||
build() {
|
||||
echo "Begin of build"
|
||||
if [[ ! -z $DRONE_TAG ]]
|
||||
then
|
||||
echo "Drone tag set, let's do a release"
|
||||
VERSION=$DRONE_TAG
|
||||
echo "${PROJECTNAME} ${VERSION}" > /build/VERSION
|
||||
elif [[ ! -z $DRONE_TAG ]]
|
||||
then
|
||||
echo "Drone not set, let's only do a build"
|
||||
VERSION=$DRONE_COMMIT
|
||||
fi
|
||||
|
||||
if [[ ! -z $VERSION && ! -z $GOOS && ! -z $GOARCH ]]
|
||||
then
|
||||
echo "Let's set a release name"
|
||||
RELEASENAME=${PROJECTNAME}-${VERSION}-${GOOS}-${GOARCH}
|
||||
fi
|
||||
|
||||
echo "Building project"
|
||||
go build -o ${PROJECTNAME} ${GOOPTIONS} ${SRCFILES}
|
||||
|
||||
if [[ ! -z $DRONE_TAG ]]
|
||||
then
|
||||
echo "Let's make archives"
|
||||
mkdir -p /build
|
||||
tar -czvf /build/${RELEASENAME}.tar.gz ${PROJECTNAME}
|
||||
fi
|
||||
|
||||
echo "Removing binary file"
|
||||
rm ${PROJECTNAME}
|
||||
|
||||
echo "End of build"
|
||||
}
|
||||
|
||||
clean() {
|
||||
rm -rf $RELEASEDIR
|
||||
}
|
||||
|
||||
case $1 in
|
||||
"build")
|
||||
build
|
||||
;;
|
||||
"clean")
|
||||
clean
|
||||
;;
|
||||
*)
|
||||
echo "No options choosen"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
53
go.mod
53
go.mod
@ -1,41 +1,42 @@
|
||||
module git.paulbsd.com/paulbsd/pki
|
||||
|
||||
go 1.17
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/go-acme/lego/v4 v4.4.0
|
||||
github.com/go-acme/lego/v4 v4.17.4
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20210406100015-1e088ea4ee04 // indirect
|
||||
github.com/labstack/echo/v4 v4.5.0
|
||||
github.com/lib/pq v1.10.3
|
||||
github.com/miekg/dns v1.1.43 // indirect
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/onsi/ginkgo v1.16.0 // indirect
|
||||
github.com/onsi/gomega v1.11.0 // indirect
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
gopkg.in/ini.v1 v1.62.1
|
||||
xorm.io/builder v0.3.9 // indirect
|
||||
xorm.io/xorm v1.2.3
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
xorm.io/builder v0.3.13 // indirect
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/goccy/go-json v0.7.8 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/labstack/gommon v0.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/ovh/go-ovh v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/oauth2 v0.22.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
|
@ -2,10 +2,13 @@ package cert
|
||||
|
||||
import "time"
|
||||
|
||||
func (e *Entry) Z() {
|
||||
}
|
||||
|
||||
// Entry is the main struct for stored certificates
|
||||
type Entry struct {
|
||||
ID int `xorm:"pk autoincr"`
|
||||
Domains string `xorm:"notnull"`
|
||||
Domain string `xorm:"notnull"`
|
||||
Certificate string `xorm:"text notnull"`
|
||||
PrivateKey string `xorm:"text notnull"`
|
||||
AuthURL string `xorm:"notnull"`
|
||||
|
@ -60,10 +60,13 @@ func (cfg *Config) GetConfig() error {
|
||||
options["ovhas"] = pkisection.Key("ovhas").MustString("")
|
||||
options["ovhck"] = pkisection.Key("ovhck").MustString("")
|
||||
|
||||
options["pdnsapiurl"] = pkisection.Key("pdnsapiurl").MustString("")
|
||||
options["pdnsapikey"] = pkisection.Key("pdnsapikey").MustString("")
|
||||
|
||||
cfg.ACME.ProviderOptions = options
|
||||
for k, v := range options {
|
||||
if v == "" {
|
||||
utils.Advice(fmt.Sprintf("OVH provider parameter %s not set", k))
|
||||
for key, value := range options {
|
||||
if value == "" {
|
||||
utils.Advice(fmt.Sprintf("Provider parameter %s not set", key))
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +75,8 @@ func (cfg *Config) GetConfig() error {
|
||||
cfg.ACME.AuthURL = lego.LEDirectoryProduction
|
||||
case "staging":
|
||||
cfg.ACME.AuthURL = lego.LEDirectoryStaging
|
||||
default:
|
||||
cfg.ACME.AuthURL = lego.LEDirectoryStaging
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"git.paulbsd.com/paulbsd/pki/src/cert"
|
||||
"git.paulbsd.com/paulbsd/pki/src/config"
|
||||
"git.paulbsd.com/paulbsd/pki/src/domain"
|
||||
"git.paulbsd.com/paulbsd/pki/src/pki"
|
||||
_ "github.com/lib/pq"
|
||||
"xorm.io/xorm"
|
||||
@ -17,7 +18,7 @@ import (
|
||||
func Init(cfg *config.Config) (err error) {
|
||||
var databaseEngine = "postgres"
|
||||
tables := []interface{}{cert.Entry{},
|
||||
pki.User{}}
|
||||
pki.User{}, domain.Domain{}}
|
||||
|
||||
cfg.Db, err = xorm.NewEngine(databaseEngine,
|
||||
fmt.Sprintf("%s://%s:%s@%s/%s",
|
||||
|
12
src/domain/main.go
Normal file
12
src/domain/main.go
Normal file
@ -0,0 +1,12 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
// Domain describes a domain
|
||||
type Domain struct {
|
||||
ID int `xorm:"pk autoincr"`
|
||||
Domain string `xorm:"text notnull unique(domain_provider)"`
|
||||
Provider string `xorm:"text notnull unique(domain_provider)"`
|
||||
Created time.Time `xorm:"created notnull"`
|
||||
Updated time.Time `xorm:"updated notnull"`
|
||||
}
|
@ -9,12 +9,13 @@ import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"git.paulbsd.com/paulbsd/pki/src/cert"
|
||||
"git.paulbsd.com/paulbsd/pki/src/config"
|
||||
"git.paulbsd.com/paulbsd/pki/src/domain"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
@ -29,15 +30,12 @@ func (u *User) Init(cfg *config.Config) (err error) {
|
||||
}
|
||||
|
||||
// GetEntry returns requested acme ressource in database relative to domain
|
||||
func (u *User) GetEntry(cfg *config.Config, domains []string) (Entry cert.Entry, err error) {
|
||||
|
||||
has, err := cfg.Db.Where("domains = ?", strings.Join(domains, ",")).And(
|
||||
func (u *User) GetEntry(cfg *config.Config, domain *string) (Entry cert.Entry, err error) {
|
||||
has, err := cfg.Db.Where("domain = ?", domain).And(
|
||||
"auth_url = ?", cfg.ACME.AuthURL).And(
|
||||
fmt.Sprintf("validity_end::timestamp-'%d DAY'::INTERVAL >= now()", cfg.ACME.MaxDaysBefore)).Desc(
|
||||
"id").Get(&Entry)
|
||||
|
||||
fmt.Println(has, err)
|
||||
|
||||
if !has {
|
||||
err = fmt.Errorf("entry doesn't exists")
|
||||
}
|
||||
@ -68,12 +66,38 @@ func (u *User) HandleRegistration(cfg *config.Config, client *lego.Client) (err
|
||||
}
|
||||
|
||||
// RequestNewCert returns a newly requested certificate to letsencrypt
|
||||
func (u *User) RequestNewCert(cfg *config.Config, domains []string) (certificates *certificate.Resource, err error) {
|
||||
func (u *User) RequestNewCert(cfg *config.Config, domainnames *[]string) (certs *certificate.Resource, err error) {
|
||||
legoconfig := lego.NewConfig(u)
|
||||
legoconfig.CADirURL = cfg.ACME.AuthURL
|
||||
legoconfig.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
ovhprovider, err := initProvider(cfg)
|
||||
var dom domain.Domain
|
||||
var has bool
|
||||
for _, d := range *domainnames {
|
||||
dom = domain.Domain{Domain: d}
|
||||
if has, err = cfg.Db.Get(&dom); has {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if !has {
|
||||
err = fmt.Errorf("supplied domain not in allowed domains")
|
||||
return
|
||||
}
|
||||
|
||||
var provider challenge.Provider
|
||||
|
||||
switch dom.Provider {
|
||||
case "ovh":
|
||||
provider, err = initOVHProvider(cfg)
|
||||
case "pdns":
|
||||
provider, err = initPowerDNSProvider(cfg)
|
||||
default:
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
@ -83,7 +107,7 @@ func (u *User) RequestNewCert(cfg *config.Config, domains []string) (certificate
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(ovhprovider)
|
||||
err = client.Challenge.SetDNS01Provider(provider)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
@ -97,14 +121,15 @@ func (u *User) RequestNewCert(cfg *config.Config, domains []string) (certificate
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
Domains: *domainnames,
|
||||
Bundle: true,
|
||||
}
|
||||
|
||||
certificates, err = client.Certificate.Obtain(request)
|
||||
certs, err = client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
package pki
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"git.paulbsd.com/paulbsd/pki/src/config"
|
||||
"github.com/go-acme/lego/v4/providers/dns/ovh"
|
||||
"github.com/go-acme/lego/v4/providers/dns/pdns"
|
||||
)
|
||||
|
||||
// initProvider initialize DNS provider configuration
|
||||
func initProvider(cfg *config.Config) (ovhprovider *ovh.DNSProvider, err error) {
|
||||
// initOVHProvider initialize DNS provider configuration
|
||||
func initOVHProvider(cfg *config.Config) (ovhprovider *ovh.DNSProvider, err error) {
|
||||
ovhconfig := ovh.NewDefaultConfig()
|
||||
|
||||
ovhconfig.APIEndpoint = cfg.ACME.ProviderOptions["ovhendpoint"]
|
||||
@ -15,6 +19,24 @@ func initProvider(cfg *config.Config) (ovhprovider *ovh.DNSProvider, err error)
|
||||
ovhconfig.ConsumerKey = cfg.ACME.ProviderOptions["ovhck"]
|
||||
|
||||
ovhprovider, err = ovh.NewDNSProviderConfig(ovhconfig)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// initPowerDNSProvider initialize DNS provider configuration
|
||||
func initPowerDNSProvider(cfg *config.Config) (pdnsprovider *pdns.DNSProvider, err error) {
|
||||
pdnsconfig := pdns.NewDefaultConfig()
|
||||
|
||||
pdnsconfig.Host, err = url.Parse(cfg.ACME.ProviderOptions["pdnsapiurl"])
|
||||
pdnsconfig.APIKey = cfg.ACME.ProviderOptions["pdnsapikey"]
|
||||
|
||||
pdnsprovider, err = pdns.NewDNSProviderConfig(pdnsconfig)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.paulbsd.com/paulbsd/pki/src/config"
|
||||
"git.paulbsd.com/paulbsd/pki/src/pki"
|
||||
@ -30,13 +29,18 @@ func RunServer(cfg *config.Config) (err error) {
|
||||
e.GET("/", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "Welcome to PKI software (https://git.paulbsd.com/paulbsd/pki)")
|
||||
})
|
||||
e.GET("/domain/:domains", func(c echo.Context) (err error) {
|
||||
var result EntryResponse
|
||||
var domains = strings.Split(c.Param("domains"), ",")
|
||||
e.POST("/cert", func(c echo.Context) (err error) {
|
||||
var request = new(EntryRequest)
|
||||
var result = make(map[string]EntryResponse)
|
||||
err = c.Bind(&request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return c.JSON(http.StatusInternalServerError, "error parsing request")
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Providing %s to user %s at %s", domains, c.Get("username"), c.RealIP()))
|
||||
log.Printf("Providing %s to user %s at %s\n", request.Domains, c.Get("username"), c.RealIP())
|
||||
|
||||
result, err = GetCertificate(cfg, c.Get("user").(*pki.User), domains)
|
||||
result, err = GetCertificate(cfg, c.Get("user").(*pki.User), &request.Domains)
|
||||
if err != nil {
|
||||
return c.String(http.StatusInternalServerError, fmt.Sprintf("%s", err))
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.paulbsd.com/paulbsd/pki/src/cert"
|
||||
@ -14,18 +13,24 @@ import (
|
||||
"git.paulbsd.com/paulbsd/pki/src/pki"
|
||||
)
|
||||
|
||||
const timeformatstring string = "2006-01-02 15:04:05"
|
||||
|
||||
var domainRegex, err = regexp.Compile(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`)
|
||||
|
||||
// GetCertificate get certificate from database if exists, of request it from ACME
|
||||
func GetCertificate(cfg *config.Config, user *pki.User, domains []string) (result EntryResponse, err error) {
|
||||
func GetCertificate(cfg *config.Config, user *pki.User, domains *[]string) (result map[string]EntryResponse, err error) {
|
||||
err = CheckDomains(domains)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = make(map[string]EntryResponse)
|
||||
|
||||
entry, err := user.GetEntry(cfg, domains)
|
||||
firstdomain := (*domains)[0]
|
||||
entry, err := user.GetEntry(cfg, &firstdomain)
|
||||
if err != nil {
|
||||
certs, err := user.RequestNewCert(cfg, domains)
|
||||
if err != nil {
|
||||
log.Println(fmt.Sprintf("Error fetching new certificate %s", err))
|
||||
log.Printf("Error fetching new certificate %s\n", err)
|
||||
return result, err
|
||||
}
|
||||
NotBefore, NotAfter, err := GetDates(certs.Certificate)
|
||||
@ -33,37 +38,40 @@ func GetCertificate(cfg *config.Config, user *pki.User, domains []string) (resul
|
||||
log.Println("Error where parsing dates")
|
||||
return result, err
|
||||
}
|
||||
entry := cert.Entry{Domains: strings.Join(domains, ","),
|
||||
entry := cert.Entry{Domain: certs.Domain,
|
||||
Certificate: string(certs.Certificate),
|
||||
PrivateKey: string(certs.PrivateKey),
|
||||
ValidityBegin: NotBefore,
|
||||
ValidityEnd: NotAfter,
|
||||
AuthURL: cfg.ACME.AuthURL}
|
||||
cfg.Db.Insert(&entry)
|
||||
result = convertEntryToResponse(entry)
|
||||
result[firstdomain] = convertEntryToResponse(entry)
|
||||
return result, err
|
||||
}
|
||||
result = convertEntryToResponse(entry)
|
||||
result[firstdomain] = convertEntryToResponse(entry)
|
||||
return
|
||||
}
|
||||
|
||||
// CheckDomains check if requested domains are valid
|
||||
func CheckDomains(domains []string) (err error) {
|
||||
domainRegex, err := regexp.Compile(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, d := range domains {
|
||||
res := domainRegex.Match([]byte(d))
|
||||
if !res {
|
||||
return fmt.Errorf(fmt.Sprintf("Domain %s has not a valid syntax %s, please verify", d, err))
|
||||
func CheckDomains(domains *[]string) (err error) {
|
||||
for _, domain := range *domains {
|
||||
err = CheckDomain(&domain)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CheckDomain check if requested domain are valid
|
||||
func CheckDomain(domain *string) (err error) {
|
||||
res := domainRegex.Match([]byte(*domain))
|
||||
if !res {
|
||||
return fmt.Errorf("Domain %s has not a valid syntax %s, please verify", *domain, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetDates decodes NotBefore and NotAfter date of cert
|
||||
func GetDates(cert []byte) (NotBefore time.Time, NotAfter time.Time, err error) {
|
||||
block, _ := pem.Decode(cert)
|
||||
@ -80,9 +88,7 @@ func GetDates(cert []byte) (NotBefore time.Time, NotAfter time.Time, err error)
|
||||
|
||||
// convertEntryToResponse converts database ACME entry to JSON ACME entry
|
||||
func convertEntryToResponse(in cert.Entry) (out EntryResponse) {
|
||||
timeformatstring := "2006-01-02 15:04:05"
|
||||
|
||||
out.Domains = in.Domains
|
||||
out.Domains = append(out.Domains, in.Domain)
|
||||
out.Certificate = in.Certificate
|
||||
out.PrivateKey = in.PrivateKey
|
||||
out.ValidityBegin = in.ValidityBegin.Format(timeformatstring)
|
||||
@ -91,11 +97,16 @@ func convertEntryToResponse(in cert.Entry) (out EntryResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
// EntryRequest
|
||||
type EntryRequest struct {
|
||||
Domains []string `json:"domains"`
|
||||
}
|
||||
|
||||
// EntryResponse is the struct defining JSON response from webservice
|
||||
type EntryResponse struct {
|
||||
Domains string `json:"domains"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"privatekey"`
|
||||
ValidityBegin string `json:"validitybegin"`
|
||||
ValidityEnd string `json:"validityend"`
|
||||
Domains []string `json:"domains"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"privatekey"`
|
||||
ValidityBegin string `json:"validitybegin"`
|
||||
ValidityEnd string `json:"validityend"`
|
||||
}
|
||||
|
10
vendor/github.com/cenkalti/backoff/v4/.travis.yml
generated
vendored
10
vendor/github.com/cenkalti/backoff/v4/.travis.yml
generated
vendored
@ -1,10 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.13
|
||||
- 1.x
|
||||
- tip
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
4
vendor/github.com/cenkalti/backoff/v4/README.md
generated
vendored
4
vendor/github.com/cenkalti/backoff/v4/README.md
generated
vendored
@ -1,4 +1,4 @@
|
||||
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
|
||||
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Coverage Status][coveralls image]][coveralls]
|
||||
|
||||
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
|
||||
|
||||
@ -21,8 +21,6 @@ Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
|
||||
|
||||
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
|
||||
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
|
||||
[travis]: https://travis-ci.org/cenkalti/backoff
|
||||
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
|
||||
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
|
||||
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
|
||||
|
||||
|
60
vendor/github.com/cenkalti/backoff/v4/exponential.go
generated
vendored
60
vendor/github.com/cenkalti/backoff/v4/exponential.go
generated
vendored
@ -71,6 +71,9 @@ type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options.
|
||||
type ExponentialBackOffOpts func(*ExponentialBackOff)
|
||||
|
||||
// Default values for ExponentialBackOff.
|
||||
const (
|
||||
DefaultInitialInterval = 500 * time.Millisecond
|
||||
@ -81,7 +84,7 @@ const (
|
||||
)
|
||||
|
||||
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||
func NewExponentialBackOff() *ExponentialBackOff {
|
||||
func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff {
|
||||
b := &ExponentialBackOff{
|
||||
InitialInterval: DefaultInitialInterval,
|
||||
RandomizationFactor: DefaultRandomizationFactor,
|
||||
@ -91,10 +94,62 @@ func NewExponentialBackOff() *ExponentialBackOff {
|
||||
Stop: Stop,
|
||||
Clock: SystemClock,
|
||||
}
|
||||
for _, fn := range opts {
|
||||
fn(b)
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
}
|
||||
|
||||
// WithInitialInterval sets the initial interval between retries.
|
||||
func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.InitialInterval = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithRandomizationFactor sets the randomization factor to add jitter to intervals.
|
||||
func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.RandomizationFactor = randomizationFactor
|
||||
}
|
||||
}
|
||||
|
||||
// WithMultiplier sets the multiplier for increasing the interval after each retry.
|
||||
func WithMultiplier(multiplier float64) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Multiplier = multiplier
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxInterval sets the maximum interval between retries.
|
||||
func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.MaxInterval = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxElapsedTime sets the maximum total time for retries.
|
||||
func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.MaxElapsedTime = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetryStopDuration sets the duration after which retries should stop.
|
||||
func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Stop = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithClockProvider sets the clock used to measure time.
|
||||
func WithClockProvider(clock Clock) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Clock = clock
|
||||
}
|
||||
}
|
||||
|
||||
type systemClock struct{}
|
||||
|
||||
func (t systemClock) Now() time.Time {
|
||||
@ -147,6 +202,9 @@ func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||
// Returns a random value from the following interval:
|
||||
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
|
||||
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||
if randomizationFactor == 0 {
|
||||
return currentInterval // make sure no randomness is used when randomizationFactor is 0.
|
||||
}
|
||||
var delta = randomizationFactor * float64(currentInterval)
|
||||
var minInterval = float64(currentInterval) - delta
|
||||
var maxInterval = float64(currentInterval) + delta
|
||||
|
50
vendor/github.com/cenkalti/backoff/v4/retry.go
generated
vendored
50
vendor/github.com/cenkalti/backoff/v4/retry.go
generated
vendored
@ -5,10 +5,20 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// An OperationWithData is executing by RetryWithData() or RetryNotifyWithData().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type OperationWithData[T any] func() (T, error)
|
||||
|
||||
// An Operation is executing by Retry() or RetryNotify().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type Operation func() error
|
||||
|
||||
func (o Operation) withEmptyData() OperationWithData[struct{}] {
|
||||
return func() (struct{}, error) {
|
||||
return struct{}{}, o()
|
||||
}
|
||||
}
|
||||
|
||||
// Notify is a notify-on-error function. It receives an operation error and
|
||||
// backoff delay if the operation failed (with an error).
|
||||
//
|
||||
@ -28,18 +38,41 @@ func Retry(o Operation, b BackOff) error {
|
||||
return RetryNotify(o, b, nil)
|
||||
}
|
||||
|
||||
// RetryWithData is like Retry but returns data in the response too.
|
||||
func RetryWithData[T any](o OperationWithData[T], b BackOff) (T, error) {
|
||||
return RetryNotifyWithData(o, b, nil)
|
||||
}
|
||||
|
||||
// RetryNotify calls notify function with the error and wait duration
|
||||
// for each failed attempt before sleep.
|
||||
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
return RetryNotifyWithTimer(operation, b, notify, nil)
|
||||
}
|
||||
|
||||
// RetryNotifyWithData is like RetryNotify but returns data in the response too.
|
||||
func RetryNotifyWithData[T any](operation OperationWithData[T], b BackOff, notify Notify) (T, error) {
|
||||
return doRetryNotify(operation, b, notify, nil)
|
||||
}
|
||||
|
||||
// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer
|
||||
// for each failed attempt before sleep.
|
||||
// A default timer that uses system timer is used when nil is passed.
|
||||
func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error {
|
||||
var err error
|
||||
var next time.Duration
|
||||
_, err := doRetryNotify(operation.withEmptyData(), b, notify, t)
|
||||
return err
|
||||
}
|
||||
|
||||
// RetryNotifyWithTimerAndData is like RetryNotifyWithTimer but returns data in the response too.
|
||||
func RetryNotifyWithTimerAndData[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
|
||||
return doRetryNotify(operation, b, notify, t)
|
||||
}
|
||||
|
||||
func doRetryNotify[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
|
||||
var (
|
||||
err error
|
||||
next time.Duration
|
||||
res T
|
||||
)
|
||||
if t == nil {
|
||||
t = &defaultTimer{}
|
||||
}
|
||||
@ -52,21 +85,22 @@ func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
if err = operation(); err == nil {
|
||||
return nil
|
||||
res, err = operation()
|
||||
if err == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var permanent *PermanentError
|
||||
if errors.As(err, &permanent) {
|
||||
return permanent.Err
|
||||
return res, permanent.Err
|
||||
}
|
||||
|
||||
if next = b.NextBackOff(); next == Stop {
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
return cerr
|
||||
return res, cerr
|
||||
}
|
||||
|
||||
return err
|
||||
return res, err
|
||||
}
|
||||
|
||||
if notify != nil {
|
||||
@ -77,7 +111,7 @@ func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
return res, ctx.Err()
|
||||
case <-t.C():
|
||||
}
|
||||
}
|
||||
|
2
vendor/github.com/go-acme/lego/v4/acme/api/account.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/acme/api/account.go
generated
vendored
@ -16,7 +16,7 @@ func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) {
|
||||
resp, err := a.core.post(a.core.GetDirectory().NewAccountURL, req, &account)
|
||||
location := getLocation(resp)
|
||||
|
||||
if len(location) > 0 {
|
||||
if location != "" {
|
||||
a.core.jws.SetKid(location)
|
||||
}
|
||||
|
||||
|
12
vendor/github.com/go-acme/lego/v4/acme/api/api.go
generated
vendored
12
vendor/github.com/go-acme/lego/v4/acme/api/api.go
generated
vendored
@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -71,7 +70,7 @@ func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response,
|
||||
}
|
||||
|
||||
// postAsGet performs an HTTP POST ("POST-as-GET") request.
|
||||
// https://tools.ietf.org/html/rfc8555#section-6.3
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-6.3
|
||||
func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) {
|
||||
return a.retrievablePost(uri, []byte{}, response)
|
||||
}
|
||||
@ -83,8 +82,6 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
||||
bo.MaxInterval = 5 * time.Second
|
||||
bo.MaxElapsedTime = 20 * time.Second
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
var resp *http.Response
|
||||
operation := func() error {
|
||||
var err error
|
||||
@ -96,8 +93,7 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
||||
return err
|
||||
}
|
||||
|
||||
cancel()
|
||||
return err
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -107,7 +103,7 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{})
|
||||
log.Infof("retry due to: %v", err)
|
||||
}
|
||||
|
||||
err := backoff.RetryNotify(operation, backoff.WithContext(bo, ctx), notify)
|
||||
err := backoff.RetryNotify(operation, bo, notify)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@ -121,7 +117,7 @@ func (a *Core) signedPost(uri string, content []byte, response interface{}) (*ht
|
||||
return nil, fmt.Errorf("failed to post JWS message: failed to sign content: %w", err)
|
||||
}
|
||||
|
||||
signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize()))
|
||||
signedBody := bytes.NewBufferString(signedContent.FullSerialize())
|
||||
|
||||
resp, err := a.doer.Post(uri, signedBody, "application/jose+json", response)
|
||||
|
||||
|
14
vendor/github.com/go-acme/lego/v4/acme/api/certificate.go
generated
vendored
14
vendor/github.com/go-acme/lego/v4/acme/api/certificate.go
generated
vendored
@ -1,10 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
@ -39,7 +40,7 @@ func (c *CertificateService) GetAll(certURL string, bundle bool) (map[string]*ac
|
||||
certs := map[string]*acme.RawCertificate{certURL: cert}
|
||||
|
||||
// URLs of "alternate" link relation
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.4.2
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2
|
||||
alts := getLinks(headers, "alternate")
|
||||
|
||||
for _, alt := range alts {
|
||||
@ -71,7 +72,7 @@ func (c *CertificateService) get(certURL string, bundle bool) (*acme.RawCertific
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
data, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
if err != nil {
|
||||
return nil, resp.Header, err
|
||||
}
|
||||
@ -87,12 +88,17 @@ func (c *CertificateService) getCertificateChain(cert []byte, headers http.Heade
|
||||
// See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962
|
||||
_, issuer := pem.Decode(cert)
|
||||
if issuer != nil {
|
||||
// If bundle is false, we want to return a single certificate.
|
||||
// To do this, we remove the issuer cert(s) from the issued cert.
|
||||
if !bundle {
|
||||
cert = bytes.TrimSuffix(cert, issuer)
|
||||
}
|
||||
return &acme.RawCertificate{Cert: cert, Issuer: issuer}
|
||||
}
|
||||
|
||||
// The issuer certificate link may be supplied via an "up" link
|
||||
// in the response headers of a new certificate.
|
||||
// See https://tools.ietf.org/html/rfc8555#section-7.4.2
|
||||
// See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2
|
||||
up := getLink(headers, "up")
|
||||
|
||||
issuer, err := c.getIssuerFromLink(up)
|
||||
|
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/nonces/nonce_manager.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/nonces/nonce_manager.go
generated
vendored
@ -63,7 +63,7 @@ func (n *Manager) getNonce() (string, error) {
|
||||
return GetFromResponse(resp)
|
||||
}
|
||||
|
||||
// GetFromResponse Extracts a nonce from a HTTP response.
|
||||
// GetFromResponse Extracts a nonce from an HTTP response.
|
||||
func GetFromResponse(resp *http.Response) (string, error) {
|
||||
if resp == nil {
|
||||
return "", errors.New("nil response")
|
||||
|
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/secure/jws.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/secure/jws.go
generated
vendored
@ -9,7 +9,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme/api/internal/nonces"
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
jose "github.com/go-jose/go-jose/v4"
|
||||
)
|
||||
|
||||
// JWS Represents a JWS.
|
||||
|
9
vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/sender.go
generated
vendored
9
vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/sender.go
generated
vendored
@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -96,7 +95,7 @@ func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, erro
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
raw, err := ioutil.ReadAll(resp.Body)
|
||||
raw, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@ -120,7 +119,7 @@ func (d *Doer) formatUserAgent() string {
|
||||
|
||||
func checkError(req *http.Request, resp *http.Response) error {
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d :: %s :: %s :: %w", resp.StatusCode, req.Method, req.URL, err)
|
||||
}
|
||||
@ -134,6 +133,10 @@ func checkError(req *http.Request, resp *http.Response) error {
|
||||
errorDetails.Method = req.Method
|
||||
errorDetails.URL = req.URL.String()
|
||||
|
||||
if errorDetails.HTTPStatus == 0 {
|
||||
errorDetails.HTTPStatus = resp.StatusCode
|
||||
}
|
||||
|
||||
// Check for errors we handle specifically
|
||||
if errorDetails.HTTPStatus == http.StatusBadRequest && errorDetails.Type == acme.BadNonceErr {
|
||||
return &acme.NonceError{ProblemDetails: errorDetails}
|
||||
|
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go
generated
vendored
@ -5,7 +5,7 @@ package sender
|
||||
|
||||
const (
|
||||
// ourUserAgent is the User-Agent of this underlying library package.
|
||||
ourUserAgent = "xenolf-acme/4.4.0"
|
||||
ourUserAgent = "xenolf-acme/4.17.4"
|
||||
|
||||
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
|
||||
// values: detach|release
|
||||
|
39
vendor/github.com/go-acme/lego/v4/acme/api/order.go
generated
vendored
39
vendor/github.com/go-acme/lego/v4/acme/api/order.go
generated
vendored
@ -3,21 +3,58 @@ package api
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
)
|
||||
|
||||
// OrderOptions used to create an order (optional).
|
||||
type OrderOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
type OrderService service
|
||||
|
||||
// New Creates a new order.
|
||||
func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) {
|
||||
return o.NewWithOptions(domains, nil)
|
||||
}
|
||||
|
||||
// NewWithOptions Creates a new order.
|
||||
func (o *OrderService) NewWithOptions(domains []string, opts *OrderOptions) (acme.ExtendedOrder, error) {
|
||||
var identifiers []acme.Identifier
|
||||
for _, domain := range domains {
|
||||
identifiers = append(identifiers, acme.Identifier{Type: "dns", Value: domain})
|
||||
ident := acme.Identifier{Value: domain, Type: "dns"}
|
||||
|
||||
if net.ParseIP(domain) != nil {
|
||||
ident.Type = "ip"
|
||||
}
|
||||
|
||||
identifiers = append(identifiers, ident)
|
||||
}
|
||||
|
||||
orderReq := acme.Order{Identifiers: identifiers}
|
||||
|
||||
if opts != nil {
|
||||
if !opts.NotAfter.IsZero() {
|
||||
orderReq.NotAfter = opts.NotAfter.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if !opts.NotBefore.IsZero() {
|
||||
orderReq.NotBefore = opts.NotBefore.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if o.core.GetDirectory().RenewalInfo != "" {
|
||||
orderReq.Replaces = opts.ReplacesCertID
|
||||
}
|
||||
}
|
||||
|
||||
var order acme.Order
|
||||
resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order)
|
||||
if err != nil {
|
||||
|
28
vendor/github.com/go-acme/lego/v4/acme/api/renewal.go
generated
vendored
Normal file
28
vendor/github.com/go-acme/lego/v4/acme/api/renewal.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrNoARI is returned when the server does not advertise a renewal info endpoint.
|
||||
var ErrNoARI = errors.New("renewalInfo[get/post]: server does not advertise a renewal info endpoint")
|
||||
|
||||
// GetRenewalInfo GETs renewal information for a certificate from the renewalInfo endpoint.
|
||||
// This is used to determine if a certificate needs to be renewed.
|
||||
//
|
||||
// Note: this endpoint is part of a draft specification, not all ACME servers will implement it.
|
||||
// This method will return api.ErrNoARI if the server does not advertise a renewal info endpoint.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/draft-ietf-acme-ari
|
||||
func (c *CertificateService) GetRenewalInfo(certID string) (*http.Response, error) {
|
||||
if c.core.GetDirectory().RenewalInfo == "" {
|
||||
return nil, ErrNoARI
|
||||
}
|
||||
|
||||
if certID == "" {
|
||||
return nil, errors.New("renewalInfo[get]: 'certID' cannot be empty")
|
||||
}
|
||||
|
||||
return c.core.HTTPClient.Get(c.core.GetDirectory().RenewalInfo + "/" + certID)
|
||||
}
|
108
vendor/github.com/go-acme/lego/v4/acme/commons.go
generated
vendored
108
vendor/github.com/go-acme/lego/v4/acme/commons.go
generated
vendored
@ -1,5 +1,5 @@
|
||||
// Package acme contains all objects related the ACME endpoints.
|
||||
// https://tools.ietf.org/html/rfc8555
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html
|
||||
package acme
|
||||
|
||||
import (
|
||||
@ -7,20 +7,38 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Challenge statuses.
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.1.6
|
||||
// ACME status values of Account, Order, Authorization and Challenge objects.
|
||||
// See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.6 for details.
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusInvalid = "invalid"
|
||||
StatusValid = "valid"
|
||||
StatusProcessing = "processing"
|
||||
StatusDeactivated = "deactivated"
|
||||
StatusExpired = "expired"
|
||||
StatusInvalid = "invalid"
|
||||
StatusPending = "pending"
|
||||
StatusProcessing = "processing"
|
||||
StatusReady = "ready"
|
||||
StatusRevoked = "revoked"
|
||||
StatusUnknown = "unknown"
|
||||
StatusValid = "valid"
|
||||
)
|
||||
|
||||
// CRL reason codes as defined in RFC 5280.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1
|
||||
const (
|
||||
CRLReasonUnspecified uint = 0
|
||||
CRLReasonKeyCompromise uint = 1
|
||||
CRLReasonCACompromise uint = 2
|
||||
CRLReasonAffiliationChanged uint = 3
|
||||
CRLReasonSuperseded uint = 4
|
||||
CRLReasonCessationOfOperation uint = 5
|
||||
CRLReasonCertificateHold uint = 6
|
||||
CRLReasonRemoveFromCRL uint = 8
|
||||
CRLReasonPrivilegeWithdrawn uint = 9
|
||||
CRLReasonAACompromise uint = 10
|
||||
)
|
||||
|
||||
// Directory the ACME directory object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.1
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1
|
||||
// - https://datatracker.ietf.org/doc/draft-ietf-acme-ari/
|
||||
type Directory struct {
|
||||
NewNonceURL string `json:"newNonce"`
|
||||
NewAccountURL string `json:"newAccount"`
|
||||
@ -29,10 +47,11 @@ type Directory struct {
|
||||
RevokeCertURL string `json:"revokeCert"`
|
||||
KeyChangeURL string `json:"keyChange"`
|
||||
Meta Meta `json:"meta"`
|
||||
RenewalInfo string `json:"renewalInfo"`
|
||||
}
|
||||
|
||||
// Meta the ACME meta object (related to Directory).
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.1
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1
|
||||
type Meta struct {
|
||||
// termsOfService (optional, string):
|
||||
// A URL identifying the current terms of service.
|
||||
@ -52,12 +71,12 @@ type Meta struct {
|
||||
|
||||
// externalAccountRequired (optional, boolean):
|
||||
// If this field is present and set to "true",
|
||||
// then the CA requires that all new- account requests include an "externalAccountBinding" field
|
||||
// then the CA requires that all new-account requests include an "externalAccountBinding" field
|
||||
// associating the new account with an external account.
|
||||
ExternalAccountRequired bool `json:"externalAccountRequired"`
|
||||
}
|
||||
|
||||
// ExtendedAccount a extended Account.
|
||||
// ExtendedAccount an extended Account.
|
||||
type ExtendedAccount struct {
|
||||
Account
|
||||
// Contains the value of the response header `Location`
|
||||
@ -65,14 +84,14 @@ type ExtendedAccount struct {
|
||||
}
|
||||
|
||||
// Account the ACME account Object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.2
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.3
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.2
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3
|
||||
type Account struct {
|
||||
// status (required, string):
|
||||
// The status of this account.
|
||||
// Possible values are: "valid", "deactivated", and "revoked".
|
||||
// The value "deactivated" should be used to indicate client-initiated deactivation
|
||||
// whereas "revoked" should be used to indicate server- initiated deactivation. (See Section 7.1.6)
|
||||
// whereas "revoked" should be used to indicate server-initiated deactivation. (See Section 7.1.6)
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
// contact (optional, array of string):
|
||||
@ -112,7 +131,7 @@ type ExtendedOrder struct {
|
||||
}
|
||||
|
||||
// Order the ACME order Object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.3
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.3
|
||||
type Order struct {
|
||||
// status (required, string):
|
||||
// The status of this order.
|
||||
@ -162,10 +181,16 @@ type Order struct {
|
||||
// certificate (optional, string):
|
||||
// A URL for the certificate that has been issued in response to this order
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
|
||||
// replaces (optional, string):
|
||||
// replaces (string, optional): A string uniquely identifying a
|
||||
// previously-issued certificate which this order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
Replaces string `json:"replaces,omitempty"`
|
||||
}
|
||||
|
||||
// Authorization the ACME authorization object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.4
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4
|
||||
type Authorization struct {
|
||||
// status (required, string):
|
||||
// The status of this authorization.
|
||||
@ -207,8 +232,8 @@ type ExtendedChallenge struct {
|
||||
}
|
||||
|
||||
// Challenge the ACME challenge object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.1.5
|
||||
// - https://tools.ietf.org/html/rfc8555#section-8
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.5
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-8
|
||||
type Challenge struct {
|
||||
// type (required, string):
|
||||
// The type of challenge encoded in the object.
|
||||
@ -241,23 +266,23 @@ type Challenge struct {
|
||||
// It MUST NOT contain any characters outside the base64url alphabet,
|
||||
// and MUST NOT include base64 padding characters ("=").
|
||||
// See [RFC4086] for additional information on randomness requirements.
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.3
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.4
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4
|
||||
Token string `json:"token"`
|
||||
|
||||
// https://tools.ietf.org/html/rfc8555#section-8.1
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.1
|
||||
KeyAuthorization string `json:"keyAuthorization"`
|
||||
}
|
||||
|
||||
// Identifier the ACME identifier object.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-9.7.7
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-9.7.7
|
||||
type Identifier struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// CSRMessage Certificate Signing Request.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.4
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4
|
||||
type CSRMessage struct {
|
||||
// csr (required, string):
|
||||
// A CSR encoding the parameters for the certificate being requested [RFC2986].
|
||||
@ -267,8 +292,8 @@ type CSRMessage struct {
|
||||
}
|
||||
|
||||
// RevokeCertMessage a certificate revocation message.
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.6
|
||||
// - https://tools.ietf.org/html/rfc5280#section-5.3.1
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.6
|
||||
// - https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1
|
||||
type RevokeCertMessage struct {
|
||||
// certificate (required, string):
|
||||
// The certificate to be revoked, in the base64url-encoded version of the DER format.
|
||||
@ -289,3 +314,36 @@ type RawCertificate struct {
|
||||
Cert []byte
|
||||
Issuer []byte
|
||||
}
|
||||
|
||||
// Window is a window of time.
|
||||
type Window struct {
|
||||
Start time.Time `json:"start"`
|
||||
End time.Time `json:"end"`
|
||||
}
|
||||
|
||||
// RenewalInfoResponse is the response to GET requests made the renewalInfo endpoint.
|
||||
// - (4.1. Getting Renewal Information) https://datatracker.ietf.org/doc/draft-ietf-acme-ari/
|
||||
type RenewalInfoResponse struct {
|
||||
// SuggestedWindow contains two fields, start and end,
|
||||
// whose values are timestamps which bound the window of time in which the CA recommends renewing the certificate.
|
||||
SuggestedWindow Window `json:"suggestedWindow"`
|
||||
// ExplanationURL is an optional URL pointing to a page which may explain why the suggested renewal window is what it is.
|
||||
// For example, it may be a page explaining the CA's dynamic load-balancing strategy,
|
||||
// or a page documenting which certificates are affected by a mass revocation event.
|
||||
// Callers SHOULD provide this URL to their operator, if present.
|
||||
ExplanationURL string `json:"explanationURL"`
|
||||
}
|
||||
|
||||
// RenewalInfoUpdateRequest is the JWS payload for POST requests made to the renewalInfo endpoint.
|
||||
// - (4.2. RenewalInfo Objects) https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-4.2
|
||||
type RenewalInfoUpdateRequest struct {
|
||||
// CertID is a composite string in the format: base64url(AKI) || '.' || base64url(Serial), where AKI is the
|
||||
// certificate's authority key identifier and Serial is the certificate's serial number. For details, see:
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-4.1
|
||||
CertID string `json:"certID"`
|
||||
// Replaced is required and indicates whether or not the client considers the certificate to have been replaced.
|
||||
// A certificate is considered replaced when its revocation would not disrupt any ongoing services,
|
||||
// for instance because it has been renewed and the new certificate is in use, or because it is no longer in use.
|
||||
// Clients SHOULD NOT send a request where this value is false.
|
||||
Replaced bool `json:"replaced"`
|
||||
}
|
||||
|
6
vendor/github.com/go-acme/lego/v4/acme/errors.go
generated
vendored
6
vendor/github.com/go-acme/lego/v4/acme/errors.go
generated
vendored
@ -11,8 +11,8 @@ const (
|
||||
)
|
||||
|
||||
// ProblemDetails the problem details object.
|
||||
// - https://tools.ietf.org/html/rfc7807#section-3.1
|
||||
// - https://tools.ietf.org/html/rfc8555#section-7.3.3
|
||||
// - https://www.rfc-editor.org/rfc/rfc7807.html#section-3.1
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3.3
|
||||
type ProblemDetails struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
@ -26,7 +26,7 @@ type ProblemDetails struct {
|
||||
}
|
||||
|
||||
// SubProblem a "subproblems".
|
||||
// - https://tools.ietf.org/html/rfc8555#section-6.7.1
|
||||
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-6.7.1
|
||||
type SubProblem struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
|
78
vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go
generated
vendored
78
vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go
generated
vendored
@ -14,6 +14,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -25,6 +27,7 @@ const (
|
||||
EC256 = KeyType("P256")
|
||||
EC384 = KeyType("P384")
|
||||
RSA2048 = KeyType("2048")
|
||||
RSA3072 = KeyType("3072")
|
||||
RSA4096 = KeyType("4096")
|
||||
RSA8192 = KeyType("8192")
|
||||
)
|
||||
@ -82,9 +85,12 @@ func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
||||
// ParsePEMPrivateKey parses a private key from key, which is a PEM block.
|
||||
// Borrowed from Go standard library, to handle various private key and PEM block types.
|
||||
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308
|
||||
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238)
|
||||
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238
|
||||
func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
keyBlockDER, _ := pem.Decode(key)
|
||||
if keyBlockDER == nil {
|
||||
return nil, errors.New("invalid PEM block")
|
||||
}
|
||||
|
||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||
@ -118,6 +124,8 @@ func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case RSA2048:
|
||||
return rsa.GenerateKey(rand.Reader, 2048)
|
||||
case RSA3072:
|
||||
return rsa.GenerateKey(rand.Reader, 3072)
|
||||
case RSA4096:
|
||||
return rsa.GenerateKey(rand.Reader, 4096)
|
||||
case RSA8192:
|
||||
@ -128,9 +136,20 @@ func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
||||
}
|
||||
|
||||
func GenerateCSR(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
|
||||
var dnsNames []string
|
||||
var ipAddresses []net.IP
|
||||
for _, altname := range san {
|
||||
if ip := net.ParseIP(altname); ip != nil {
|
||||
ipAddresses = append(ipAddresses, ip)
|
||||
} else {
|
||||
dnsNames = append(dnsNames, altname)
|
||||
}
|
||||
}
|
||||
|
||||
template := x509.CertificateRequest{
|
||||
Subject: pkix.Name{CommonName: domain},
|
||||
DNSNames: san,
|
||||
Subject: pkix.Name{CommonName: domain},
|
||||
DNSNames: dnsNames,
|
||||
IPAddresses: ipAddresses,
|
||||
}
|
||||
|
||||
if mustStaple {
|
||||
@ -198,6 +217,26 @@ func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) {
|
||||
return x509.ParseCertificate(pemBlock.Bytes)
|
||||
}
|
||||
|
||||
func GetCertificateMainDomain(cert *x509.Certificate) (string, error) {
|
||||
return getMainDomain(cert.Subject, cert.DNSNames)
|
||||
}
|
||||
|
||||
func GetCSRMainDomain(cert *x509.CertificateRequest) (string, error) {
|
||||
return getMainDomain(cert.Subject, cert.DNSNames)
|
||||
}
|
||||
|
||||
func getMainDomain(subject pkix.Name, dnsNames []string) (string, error) {
|
||||
if subject.CommonName == "" && len(dnsNames) == 0 {
|
||||
return "", errors.New("missing domain")
|
||||
}
|
||||
|
||||
if subject.CommonName != "" {
|
||||
return subject.CommonName, nil
|
||||
}
|
||||
|
||||
return dnsNames[0], nil
|
||||
}
|
||||
|
||||
func ExtractDomains(cert *x509.Certificate) []string {
|
||||
var domains []string
|
||||
if cert.Subject.CommonName != "" {
|
||||
@ -212,6 +251,13 @@ func ExtractDomains(cert *x509.Certificate) []string {
|
||||
domains = append(domains, sanDomain)
|
||||
}
|
||||
|
||||
commonNameIP := net.ParseIP(cert.Subject.CommonName)
|
||||
for _, sanIP := range cert.IPAddresses {
|
||||
if !commonNameIP.Equal(sanIP) {
|
||||
domains = append(domains, sanIP.String())
|
||||
}
|
||||
}
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
@ -223,7 +269,7 @@ func ExtractDomainsCSR(csr *x509.CertificateRequest) []string {
|
||||
|
||||
// loop over the SubjectAltName DNS names
|
||||
for _, sanName := range csr.DNSNames {
|
||||
if containsSAN(domains, sanName) {
|
||||
if slices.Contains(domains, sanName) {
|
||||
// Duplicate; skip this name
|
||||
continue
|
||||
}
|
||||
@ -232,16 +278,14 @@ func ExtractDomainsCSR(csr *x509.CertificateRequest) []string {
|
||||
domains = append(domains, sanName)
|
||||
}
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
func containsSAN(domains []string, sanName string) bool {
|
||||
for _, existingName := range domains {
|
||||
if existingName == sanName {
|
||||
return true
|
||||
cnip := net.ParseIP(csr.Subject.CommonName)
|
||||
for _, sanIP := range csr.IPAddresses {
|
||||
if !cnip.Equal(sanIP) {
|
||||
domains = append(domains, sanIP.String())
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
func GeneratePemCert(privateKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) {
|
||||
@ -261,7 +305,7 @@ func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain st
|
||||
}
|
||||
|
||||
if expiration.IsZero() {
|
||||
expiration = time.Now().Add(365)
|
||||
expiration = time.Now().AddDate(1, 0, 0)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
@ -274,9 +318,15 @@ func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain st
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
ExtraExtensions: extensions,
|
||||
}
|
||||
|
||||
// handling SAN filling as type suspected
|
||||
if ip := net.ParseIP(domain); ip != nil {
|
||||
template.IPAddresses = []net.IP{ip}
|
||||
} else {
|
||||
template.DNSNames = []string{domain}
|
||||
}
|
||||
|
||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
}
|
||||
|
19
vendor/github.com/go-acme/lego/v4/certificate/authorization.go
generated
vendored
19
vendor/github.com/go-acme/lego/v4/certificate/authorization.go
generated
vendored
@ -12,6 +12,7 @@ const (
|
||||
// limited on the "new-reg", "new-authz" and "new-cert" endpoints.
|
||||
// From the documentation the limitation is 20 requests per second,
|
||||
// but using 20 as value doesn't work but 18 do.
|
||||
// https://letsencrypt.org/docs/rate-limits/
|
||||
overallRequestLimit = 18
|
||||
)
|
||||
|
||||
@ -35,13 +36,14 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
|
||||
}
|
||||
|
||||
var responses []acme.Authorization
|
||||
failures := make(obtainError)
|
||||
for i := 0; i < len(order.Authorizations); i++ {
|
||||
|
||||
failures := newObtainError()
|
||||
for range len(order.Authorizations) {
|
||||
select {
|
||||
case res := <-resc:
|
||||
responses = append(responses, res)
|
||||
case err := <-errc:
|
||||
failures[err.Domain] = err.Error
|
||||
failures.Add(err.Domain, err.Error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,15 +54,10 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
|
||||
close(resc)
|
||||
close(errc)
|
||||
|
||||
// be careful to not return an empty failures map;
|
||||
// even if empty, they become non-nil error values
|
||||
if len(failures) > 0 {
|
||||
return responses, failures
|
||||
}
|
||||
return responses, nil
|
||||
return responses, failures.Join()
|
||||
}
|
||||
|
||||
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) {
|
||||
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) {
|
||||
for _, authzURL := range order.Authorizations {
|
||||
auth, err := c.core.Authorizations.Get(authzURL)
|
||||
if err != nil {
|
||||
@ -68,7 +65,7 @@ func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) {
|
||||
continue
|
||||
}
|
||||
|
||||
if auth.Status == acme.StatusValid {
|
||||
if auth.Status == acme.StatusValid && !force {
|
||||
log.Infof("Skipping deactivating of valid auth: %s", authzURL)
|
||||
continue
|
||||
}
|
||||
|
200
vendor/github.com/go-acme/lego/v4/certificate/certificates.go
generated
vendored
200
vendor/github.com/go-acme/lego/v4/certificate/certificates.go
generated
vendored
@ -7,7 +7,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@ -49,22 +49,44 @@ type Resource struct {
|
||||
// If you do not want that you can supply your own private key in the privateKey parameter.
|
||||
// If this parameter is non-nil it will be used instead of generating a new one.
|
||||
//
|
||||
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
// If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
|
||||
//
|
||||
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
|
||||
type ObtainRequest struct {
|
||||
Domains []string
|
||||
Bundle bool
|
||||
PrivateKey crypto.PrivateKey
|
||||
MustStaple bool
|
||||
PreferredChain string
|
||||
Domains []string
|
||||
PrivateKey crypto.PrivateKey
|
||||
MustStaple bool
|
||||
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
// ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it.
|
||||
//
|
||||
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
// If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
|
||||
//
|
||||
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
|
||||
type ObtainForCSRRequest struct {
|
||||
CSR *x509.CertificateRequest
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
CSR *x509.CertificateRequest
|
||||
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// A string uniquely identifying a previously-issued certificate which this
|
||||
// order is intended to replace.
|
||||
// - https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
|
||||
ReplacesCertID string
|
||||
}
|
||||
|
||||
type resolver interface {
|
||||
@ -109,7 +131,13 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
||||
log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.New(domains)
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
ReplacesCertID: request.ReplacesCertID,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -117,33 +145,32 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
||||
authz, err := c.getAuthorizations(order)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
c.deactivateAuthorizations(order)
|
||||
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.resolver.Solve(authz)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
c.deactivateAuthorizations(order)
|
||||
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(obtainError)
|
||||
failures := newObtainError()
|
||||
cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple, request.PreferredChain)
|
||||
if err != nil {
|
||||
for _, auth := range authz {
|
||||
failures[challenge.GetTargetedDomain(auth)] = err
|
||||
failures.Add(challenge.GetTargetedDomain(auth), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Do not return an empty failures map, because
|
||||
// it would still be a non-nil error value
|
||||
if len(failures) > 0 {
|
||||
return cert, failures
|
||||
if request.AlwaysDeactivateAuthorizations {
|
||||
c.deactivateAuthorizations(order, true)
|
||||
}
|
||||
return cert, nil
|
||||
|
||||
return cert, failures.Join()
|
||||
}
|
||||
|
||||
// ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
|
||||
@ -170,7 +197,13 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
||||
log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.New(domains)
|
||||
orderOpts := &api.OrderOptions{
|
||||
NotBefore: request.NotBefore,
|
||||
NotAfter: request.NotAfter,
|
||||
ReplacesCertID: request.ReplacesCertID,
|
||||
}
|
||||
|
||||
order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -178,38 +211,37 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
||||
authz, err := c.getAuthorizations(order)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
c.deactivateAuthorizations(order)
|
||||
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.resolver.Solve(authz)
|
||||
if err != nil {
|
||||
// If any challenge fails, return. Do not generate partial SAN certificates.
|
||||
c.deactivateAuthorizations(order)
|
||||
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
failures := make(obtainError)
|
||||
failures := newObtainError()
|
||||
cert, err := c.getForCSR(domains, order, request.Bundle, request.CSR.Raw, nil, request.PreferredChain)
|
||||
if err != nil {
|
||||
for _, auth := range authz {
|
||||
failures[challenge.GetTargetedDomain(auth)] = err
|
||||
failures.Add(challenge.GetTargetedDomain(auth), err)
|
||||
}
|
||||
}
|
||||
|
||||
if request.AlwaysDeactivateAuthorizations {
|
||||
c.deactivateAuthorizations(order, true)
|
||||
}
|
||||
|
||||
if cert != nil {
|
||||
// Add the CSR to the certificate so that it can be used for renewals.
|
||||
cert.CSR = certcrypto.PEMEncode(request.CSR)
|
||||
}
|
||||
|
||||
// Do not return an empty failures map,
|
||||
// because it would still be a non-nil error value
|
||||
if len(failures) > 0 {
|
||||
return cert, failures
|
||||
}
|
||||
return cert, nil
|
||||
return cert, failures.Join()
|
||||
}
|
||||
|
||||
func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool, preferredChain string) (*Resource, error) {
|
||||
@ -221,16 +253,23 @@ func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bund
|
||||
}
|
||||
}
|
||||
|
||||
// Determine certificate name(s) based on the authorization resources
|
||||
commonName := domains[0]
|
||||
commonName := ""
|
||||
if len(domains[0]) <= 64 {
|
||||
commonName = domains[0]
|
||||
}
|
||||
|
||||
// RFC8555 Section 7.4 "Applying for Certificate Issuance"
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.4
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4
|
||||
// says:
|
||||
// Clients SHOULD NOT make any assumptions about the sort order of
|
||||
// "identifiers" or "authorizations" elements in the returned order
|
||||
// object.
|
||||
san := []string{commonName}
|
||||
|
||||
var san []string
|
||||
if commonName != "" {
|
||||
san = append(san, commonName)
|
||||
}
|
||||
|
||||
for _, auth := range order.Identifiers {
|
||||
if auth.Value != commonName {
|
||||
san = append(san, auth.Value)
|
||||
@ -252,15 +291,14 @@ func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commonName := domains[0]
|
||||
certRes := &Resource{
|
||||
Domain: commonName,
|
||||
Domain: domains[0],
|
||||
CertURL: respOrder.Certificate,
|
||||
PrivateKey: privateKeyPem,
|
||||
}
|
||||
|
||||
if respOrder.Status == acme.StatusValid {
|
||||
// if the certificate is available right away, short cut!
|
||||
// if the certificate is available right away, shortcut!
|
||||
ok, errR := c.checkResponse(respOrder, certRes, bundle, preferredChain)
|
||||
if errR != nil {
|
||||
return nil, errR
|
||||
@ -349,6 +387,11 @@ func (c *Certifier) checkResponse(order acme.ExtendedOrder, certRes *Resource, b
|
||||
|
||||
// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||
func (c *Certifier) Revoke(cert []byte) error {
|
||||
return c.RevokeWithReason(cert, nil)
|
||||
}
|
||||
|
||||
// RevokeWithReason takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||
func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) error {
|
||||
certificates, err := certcrypto.ParsePEMBundle(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -361,11 +404,24 @@ func (c *Certifier) Revoke(cert []byte) error {
|
||||
|
||||
revokeMsg := acme.RevokeCertMessage{
|
||||
Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw),
|
||||
Reason: reason,
|
||||
}
|
||||
|
||||
return c.core.Certificates.Revoke(revokeMsg)
|
||||
}
|
||||
|
||||
// RenewOptions options used by Certifier.RenewWithOptions.
|
||||
type RenewOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
// If true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
Bundle bool
|
||||
PreferredChain string
|
||||
AlwaysDeactivateAuthorizations bool
|
||||
// Not supported for CSR request.
|
||||
MustStaple bool
|
||||
}
|
||||
|
||||
// Renew takes a Resource and tries to renew the certificate.
|
||||
//
|
||||
// If the renewal process succeeds, the new certificate will be returned in a new CertResource.
|
||||
@ -376,7 +432,26 @@ func (c *Certifier) Revoke(cert []byte) error {
|
||||
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
//
|
||||
// For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
|
||||
// Deprecated: use RenewWithOptions instead.
|
||||
func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredChain string) (*Resource, error) {
|
||||
return c.RenewWithOptions(certRes, &RenewOptions{
|
||||
Bundle: bundle,
|
||||
PreferredChain: preferredChain,
|
||||
MustStaple: mustStaple,
|
||||
})
|
||||
}
|
||||
|
||||
// RenewWithOptions takes a Resource and tries to renew the certificate.
|
||||
//
|
||||
// If the renewal process succeeds, the new certificate will be returned in a new CertResource.
|
||||
// Please be aware that this function will return a new certificate in ANY case that is not an error.
|
||||
// If the server does not provide us with a new cert on a GET request to the CertURL
|
||||
// this function will start a new-cert flow where a new certificate gets generated.
|
||||
//
|
||||
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
|
||||
//
|
||||
// For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
|
||||
func (c *Certifier) RenewWithOptions(certRes Resource, options *RenewOptions) (*Resource, error) {
|
||||
// Input certificate is PEM encoded.
|
||||
// Decode it here as we may need the decoded cert later on in the renewal process.
|
||||
// The input may be a bundle or a single certificate.
|
||||
@ -403,11 +478,17 @@ func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredCh
|
||||
return nil, errP
|
||||
}
|
||||
|
||||
return c.ObtainForCSR(ObtainForCSRRequest{
|
||||
CSR: csr,
|
||||
Bundle: bundle,
|
||||
PreferredChain: preferredChain,
|
||||
})
|
||||
request := ObtainForCSRRequest{CSR: csr}
|
||||
|
||||
if options != nil {
|
||||
request.NotBefore = options.NotBefore
|
||||
request.NotAfter = options.NotAfter
|
||||
request.Bundle = options.Bundle
|
||||
request.PreferredChain = options.PreferredChain
|
||||
request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
|
||||
}
|
||||
|
||||
return c.ObtainForCSR(request)
|
||||
}
|
||||
|
||||
var privateKey crypto.PrivateKey
|
||||
@ -418,13 +499,21 @@ func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredCh
|
||||
}
|
||||
}
|
||||
|
||||
query := ObtainRequest{
|
||||
request := ObtainRequest{
|
||||
Domains: certcrypto.ExtractDomains(x509Cert),
|
||||
Bundle: bundle,
|
||||
PrivateKey: privateKey,
|
||||
MustStaple: mustStaple,
|
||||
}
|
||||
return c.Obtain(query)
|
||||
|
||||
if options != nil {
|
||||
request.MustStaple = options.MustStaple
|
||||
request.NotBefore = options.NotBefore
|
||||
request.NotAfter = options.NotAfter
|
||||
request.Bundle = options.Bundle
|
||||
request.PreferredChain = options.PreferredChain
|
||||
request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
|
||||
}
|
||||
|
||||
return c.Obtain(request)
|
||||
}
|
||||
|
||||
// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response,
|
||||
@ -465,7 +554,7 @@ func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
issuerBytes, errC := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
issuerBytes, errC := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
if errC != nil {
|
||||
return nil, nil, errC
|
||||
}
|
||||
@ -494,7 +583,7 @@ func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
ocspResBytes, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
ocspResBytes, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -525,8 +614,13 @@ func (c *Certifier) Get(url string, bundle bool) (*Resource, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domain, err := certcrypto.GetCertificateMainDomain(x509Certs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Resource{
|
||||
Domain: x509Certs[0].Subject.CommonName,
|
||||
Domain: domain,
|
||||
Certificate: cert,
|
||||
IssuerCertificate: issuer,
|
||||
CertURL: url,
|
||||
@ -560,11 +654,11 @@ func checkOrderStatus(order acme.ExtendedOrder) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc8555#section-7.1.4
|
||||
// https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4
|
||||
// The domain name MUST be encoded in the form in which it would appear in a certificate.
|
||||
// That is, it MUST be encoded according to the rules in Section 7 of [RFC5280].
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-7
|
||||
// https://www.rfc-editor.org/rfc/rfc5280.html#section-7
|
||||
func sanitizeDomain(domains []string) []string {
|
||||
var sanitizedDomains []string
|
||||
for _, domain := range domains {
|
||||
|
36
vendor/github.com/go-acme/lego/v4/certificate/errors.go
generated
vendored
36
vendor/github.com/go-acme/lego/v4/certificate/errors.go
generated
vendored
@ -1,27 +1,37 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// obtainError is returned when there are specific errors available per domain.
|
||||
type obtainError map[string]error
|
||||
type obtainError struct {
|
||||
data map[string]error
|
||||
}
|
||||
|
||||
func (e obtainError) Error() string {
|
||||
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
|
||||
func newObtainError() *obtainError {
|
||||
return &obtainError{data: make(map[string]error)}
|
||||
}
|
||||
|
||||
var domains []string
|
||||
for domain := range e {
|
||||
domains = append(domains, domain)
|
||||
func (e *obtainError) Add(domain string, err error) {
|
||||
e.data[domain] = err
|
||||
}
|
||||
|
||||
func (e *obtainError) Join() error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
sort.Strings(domains)
|
||||
|
||||
for _, domain := range domains {
|
||||
buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain]))
|
||||
if len(e.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return buffer.String()
|
||||
|
||||
var err error
|
||||
for d, e := range e.data {
|
||||
err = errors.Join(err, fmt.Errorf("%s: %w", d, e))
|
||||
}
|
||||
|
||||
return fmt.Errorf("error: one or more domains had a problem:\n%w", err)
|
||||
}
|
||||
|
||||
type domainError struct {
|
||||
|
129
vendor/github.com/go-acme/lego/v4/certificate/renewal.go
generated
vendored
Normal file
129
vendor/github.com/go-acme/lego/v4/certificate/renewal.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
)
|
||||
|
||||
// RenewalInfoRequest contains the necessary renewal information.
|
||||
type RenewalInfoRequest struct {
|
||||
Cert *x509.Certificate
|
||||
}
|
||||
|
||||
// RenewalInfoResponse is a wrapper around acme.RenewalInfoResponse that provides a method for determining when to renew a certificate.
|
||||
type RenewalInfoResponse struct {
|
||||
acme.RenewalInfoResponse
|
||||
|
||||
// RetryAfter header indicating the polling interval that the ACME server recommends.
|
||||
// Conforming clients SHOULD query the renewalInfo URL again after the RetryAfter period has passed,
|
||||
// as the server may provide a different suggestedWindow.
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-4.2
|
||||
RetryAfter time.Duration
|
||||
}
|
||||
|
||||
// ShouldRenewAt determines the optimal renewal time based on the current time (UTC),renewal window suggest by ARI, and the client's willingness to sleep.
|
||||
// It returns a pointer to a time.Time value indicating when the renewal should be attempted or nil if deferred until the next normal wake time.
|
||||
// This method implements the RECOMMENDED algorithm described in draft-ietf-acme-ari.
|
||||
//
|
||||
// - (4.1-11. Getting Renewal Information) https://datatracker.ietf.org/doc/draft-ietf-acme-ari/
|
||||
func (r *RenewalInfoResponse) ShouldRenewAt(now time.Time, willingToSleep time.Duration) *time.Time {
|
||||
// Explicitly convert all times to UTC.
|
||||
now = now.UTC()
|
||||
start := r.SuggestedWindow.Start.UTC()
|
||||
end := r.SuggestedWindow.End.UTC()
|
||||
|
||||
// Select a uniform random time within the suggested window.
|
||||
window := end.Sub(start)
|
||||
randomDuration := time.Duration(rand.Int63n(int64(window)))
|
||||
rt := start.Add(randomDuration)
|
||||
|
||||
// If the selected time is in the past, attempt renewal immediately.
|
||||
if rt.Before(now) {
|
||||
return &now
|
||||
}
|
||||
|
||||
// Otherwise, if the client can schedule itself to attempt renewal at exactly the selected time, do so.
|
||||
willingToSleepUntil := now.Add(willingToSleep)
|
||||
if willingToSleepUntil.After(rt) || willingToSleepUntil.Equal(rt) {
|
||||
return &rt
|
||||
}
|
||||
|
||||
// TODO: Otherwise, if the selected time is before the next time that the client would wake up normally, attempt renewal immediately.
|
||||
|
||||
// Otherwise, sleep until the next normal wake time, re-check ARI, and return to Step 1.
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRenewalInfo sends a request to the ACME server's renewalInfo endpoint to obtain a suggested renewal window.
|
||||
// The caller MUST provide the certificate and issuer certificate for the certificate they wish to renew.
|
||||
// The caller should attempt to renew the certificate at the time indicated by the ShouldRenewAt method of the returned RenewalInfoResponse object.
|
||||
//
|
||||
// Note: this endpoint is part of a draft specification, not all ACME servers will implement it.
|
||||
// This method will return api.ErrNoARI if the server does not advertise a renewal info endpoint.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/draft-ietf-acme-ari
|
||||
func (c *Certifier) GetRenewalInfo(req RenewalInfoRequest) (*RenewalInfoResponse, error) {
|
||||
certID, err := MakeARICertID(req.Cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making certID: %w", err)
|
||||
}
|
||||
|
||||
resp, err := c.core.Certificates.GetRenewalInfo(certID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var info RenewalInfoResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if retry := resp.Header.Get("Retry-After"); retry != "" {
|
||||
info.RetryAfter, err = time.ParseDuration(retry + "s")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// MakeARICertID constructs a certificate identifier as described in draft-ietf-acme-ari-03, section 4.1.
|
||||
func MakeARICertID(leaf *x509.Certificate) (string, error) {
|
||||
if leaf == nil {
|
||||
return "", errors.New("leaf certificate is nil")
|
||||
}
|
||||
|
||||
// Marshal the Serial Number into DER.
|
||||
der, err := asn1.Marshal(leaf.SerialNumber)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Check if the DER encoded bytes are sufficient (at least 3 bytes: tag,
|
||||
// length, and value).
|
||||
if len(der) < 3 {
|
||||
return "", errors.New("invalid DER encoding of serial number")
|
||||
}
|
||||
|
||||
// Extract only the integer bytes from the DER encoded Serial Number
|
||||
// Skipping the first 2 bytes (tag and length).
|
||||
serial := base64.RawURLEncoding.EncodeToString(der[2:])
|
||||
|
||||
// Convert the Authority Key Identifier to base64url encoding without
|
||||
// padding.
|
||||
aki := base64.RawURLEncoding.EncodeToString(leaf.AuthorityKeyId)
|
||||
|
||||
// Construct the final identifier by concatenating AKI and Serial Number.
|
||||
return fmt.Sprintf("%s.%s", aki, serial), nil
|
||||
}
|
6
vendor/github.com/go-acme/lego/v4/challenge/challenges.go
generated
vendored
6
vendor/github.com/go-acme/lego/v4/challenge/challenges.go
generated
vendored
@ -10,15 +10,15 @@ import (
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.3
|
||||
// HTTP01 is the "http-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3
|
||||
// Note: ChallengePath returns the URL path to fulfill this challenge.
|
||||
HTTP01 = Type("http-01")
|
||||
|
||||
// DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.4
|
||||
// DNS01 is the "dns-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4
|
||||
// Note: GetRecord returns a DNS record which will fulfill this challenge.
|
||||
DNS01 = Type("dns-01")
|
||||
|
||||
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07
|
||||
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8737.html
|
||||
TLSALPN01 = Type("tls-alpn-01")
|
||||
)
|
||||
|
||||
|
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/cname.go
generated
vendored
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/cname.go
generated
vendored
@ -1,12 +1,16 @@
|
||||
package dns01
|
||||
|
||||
import "github.com/miekg/dns"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Update FQDN with CNAME if any.
|
||||
func updateDomainWithCName(r *dns.Msg, fqdn string) string {
|
||||
for _, rr := range r.Answer {
|
||||
if cn, ok := rr.(*dns.CNAME); ok {
|
||||
if cn.Hdr.Name == fqdn {
|
||||
if strings.EqualFold(cn.Hdr.Name, fqdn) {
|
||||
return cn.Target
|
||||
}
|
||||
}
|
||||
|
73
vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge.go
generated
vendored
73
vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge.go
generated
vendored
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/acme"
|
||||
@ -114,7 +115,7 @@ func (c *Challenge) Solve(authz acme.Authorization) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fqdn, value := GetRecord(authz.Identifier.Value, keyAuth)
|
||||
info := GetChallengeInfo(authz.Identifier.Value, keyAuth)
|
||||
|
||||
var timeout, interval time.Duration
|
||||
switch provider := c.provider.(type) {
|
||||
@ -124,12 +125,12 @@ func (c *Challenge) Solve(authz acme.Authorization) error {
|
||||
timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval
|
||||
}
|
||||
|
||||
log.Infof("[%s] acme: Checking DNS record propagation using %+v", domain, recursiveNameservers)
|
||||
log.Infof("[%s] acme: Checking DNS record propagation. [nameservers=%s]", domain, strings.Join(recursiveNameservers, ","))
|
||||
|
||||
time.Sleep(interval)
|
||||
|
||||
err = wait.For("propagation", timeout, interval, func() (bool, error) {
|
||||
stop, errP := c.preCheck.call(domain, fqdn, value)
|
||||
stop, errP := c.preCheck.call(domain, info.EffectiveFQDN, info.Value)
|
||||
if !stop || errP != nil {
|
||||
log.Infof("[%s] acme: Waiting for DNS record propagation.", domain)
|
||||
}
|
||||
@ -172,19 +173,67 @@ type sequential interface {
|
||||
}
|
||||
|
||||
// GetRecord returns a DNS record which will fulfill the `dns-01` challenge.
|
||||
// Deprecated: use GetChallengeInfo instead.
|
||||
func GetRecord(domain, keyAuth string) (fqdn, value string) {
|
||||
info := GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
return info.EffectiveFQDN, info.Value
|
||||
}
|
||||
|
||||
// ChallengeInfo contains the information use to create the TXT record.
|
||||
type ChallengeInfo struct {
|
||||
// FQDN is the full-qualified challenge domain (i.e. `_acme-challenge.[domain].`)
|
||||
FQDN string
|
||||
|
||||
// EffectiveFQDN contains the resulting FQDN after the CNAMEs resolutions.
|
||||
EffectiveFQDN string
|
||||
|
||||
// Value contains the value for the TXT record.
|
||||
Value string
|
||||
}
|
||||
|
||||
// GetChallengeInfo returns information used to create a DNS record which will fulfill the `dns-01` challenge.
|
||||
func GetChallengeInfo(domain, keyAuth string) ChallengeInfo {
|
||||
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
|
||||
// base64URL encoding without padding
|
||||
value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
|
||||
fqdn = fmt.Sprintf("_acme-challenge.%s.", domain)
|
||||
value := base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
|
||||
|
||||
if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_CNAME_SUPPORT")); ok {
|
||||
r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true)
|
||||
// Check if the domain has CNAME then return that
|
||||
if err == nil && r.Rcode == dns.RcodeSuccess {
|
||||
fqdn = updateDomainWithCName(r, fqdn)
|
||||
}
|
||||
ok, _ := strconv.ParseBool(os.Getenv("LEGO_DISABLE_CNAME_SUPPORT"))
|
||||
|
||||
return ChallengeInfo{
|
||||
Value: value,
|
||||
FQDN: getChallengeFQDN(domain, false),
|
||||
EffectiveFQDN: getChallengeFQDN(domain, !ok),
|
||||
}
|
||||
}
|
||||
|
||||
func getChallengeFQDN(domain string, followCNAME bool) string {
|
||||
fqdn := fmt.Sprintf("_acme-challenge.%s.", domain)
|
||||
|
||||
if !followCNAME {
|
||||
return fqdn
|
||||
}
|
||||
|
||||
return
|
||||
// recursion counter so it doesn't spin out of control
|
||||
for range 50 {
|
||||
// Keep following CNAMEs
|
||||
r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true)
|
||||
|
||||
if err != nil || r.Rcode != dns.RcodeSuccess {
|
||||
// No more CNAME records to follow, exit
|
||||
break
|
||||
}
|
||||
|
||||
// Check if the domain has CNAME then use that
|
||||
cname := updateDomainWithCName(r, fqdn)
|
||||
if cname == fqdn {
|
||||
break
|
||||
}
|
||||
|
||||
log.Infof("Found CNAME entry for %q: %q", fqdn, cname)
|
||||
|
||||
fqdn = cname
|
||||
}
|
||||
|
||||
return fqdn
|
||||
}
|
||||
|
23
vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go
generated
vendored
23
vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go
generated
vendored
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
dnsTemplate = `%s %d IN TXT "%s"`
|
||||
dnsTemplate = `%s %d IN TXT %q`
|
||||
)
|
||||
|
||||
// DNSProviderManual is an implementation of the ChallengeProvider interface.
|
||||
@ -21,33 +21,36 @@ func NewDNSProviderManual() (*DNSProviderManual, error) {
|
||||
|
||||
// Present prints instructions for manually creating the TXT record.
|
||||
func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := GetRecord(domain, keyAuth)
|
||||
info := GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := FindZoneByFqdn(fqdn)
|
||||
authZone, err := FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("manual: could not find zone: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("lego: Please create the following TXT record in your %s zone:\n", authZone)
|
||||
fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, value)
|
||||
fmt.Printf(dnsTemplate+"\n", info.EffectiveFQDN, DefaultTTL, info.Value)
|
||||
fmt.Printf("lego: Press 'Enter' when you are done\n")
|
||||
|
||||
_, err = bufio.NewReader(os.Stdin).ReadBytes('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("manual: %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp prints instructions for manually removing the TXT record.
|
||||
func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _ := GetRecord(domain, keyAuth)
|
||||
info := GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := FindZoneByFqdn(fqdn)
|
||||
authZone, err := FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("manual: could not find zone: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("lego: You can now remove this TXT record from your %s zone:\n", authZone)
|
||||
fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, "...")
|
||||
fmt.Printf(dnsTemplate+"\n", info.EffectiveFQDN, DefaultTTL, "...")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
24
vendor/github.com/go-acme/lego/v4/challenge/dns01/domain.go
generated
vendored
Normal file
24
vendor/github.com/go-acme/lego/v4/challenge/dns01/domain.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package dns01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// ExtractSubDomain extracts the subdomain part from a domain and a zone.
|
||||
func ExtractSubDomain(domain, zone string) (string, error) {
|
||||
canonDomain := dns.Fqdn(domain)
|
||||
canonZone := dns.Fqdn(zone)
|
||||
|
||||
if canonDomain == canonZone {
|
||||
return "", fmt.Errorf("no subdomain because the domain and the zone are identical: %s", canonDomain)
|
||||
}
|
||||
|
||||
if !dns.IsSubDomain(canonZone, canonDomain) {
|
||||
return "", fmt.Errorf("%s is not a subdomain of %s", canonDomain, canonZone)
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(canonDomain, "."+canonZone), nil
|
||||
}
|
155
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver.go
generated
vendored
155
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver.go
generated
vendored
@ -4,6 +4,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -13,9 +16,6 @@ import (
|
||||
|
||||
const defaultResolvConf = "/etc/resolv.conf"
|
||||
|
||||
// dnsTimeout is used to override the default DNS timeout of 10 seconds.
|
||||
var dnsTimeout = 10 * time.Second
|
||||
|
||||
var (
|
||||
fqdnSoaCache = map[string]*soaCacheEntry{}
|
||||
muFqdnSoaCache sync.Mutex
|
||||
@ -99,12 +99,12 @@ func lookupNameservers(fqdn string) ([]string, error) {
|
||||
|
||||
zone, err := FindZoneByFqdn(fqdn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine the zone: %w", err)
|
||||
return nil, fmt.Errorf("could not find zone: %w", err)
|
||||
}
|
||||
|
||||
r, err := dnsQuery(zone, dns.TypeNS, recursiveNameservers, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("NS call failed: %w", err)
|
||||
}
|
||||
|
||||
for _, rr := range r.Answer {
|
||||
@ -116,7 +116,8 @@ func lookupNameservers(fqdn string) ([]string, error) {
|
||||
if len(authoritativeNss) > 0 {
|
||||
return authoritativeNss, nil
|
||||
}
|
||||
return nil, errors.New("could not determine authoritative nameservers")
|
||||
|
||||
return nil, fmt.Errorf("[zone=%s] could not determine authoritative nameservers", zone)
|
||||
}
|
||||
|
||||
// FindPrimaryNsByFqdn determines the primary nameserver of the zone apex for the given fqdn
|
||||
@ -130,7 +131,7 @@ func FindPrimaryNsByFqdn(fqdn string) (string, error) {
|
||||
func FindPrimaryNsByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
||||
soa, err := lookupSoaByFqdn(fqdn, nameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("[fqdn=%s] %w", fqdn, err)
|
||||
}
|
||||
return soa.primaryNs, nil
|
||||
}
|
||||
@ -146,7 +147,7 @@ func FindZoneByFqdn(fqdn string) (string, error) {
|
||||
func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) {
|
||||
soa, err := lookupSoaByFqdn(fqdn, nameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("[fqdn=%s] %w", fqdn, err)
|
||||
}
|
||||
return soa.zone, nil
|
||||
}
|
||||
@ -171,35 +172,35 @@ func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error)
|
||||
|
||||
func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
|
||||
var err error
|
||||
var in *dns.Msg
|
||||
var r *dns.Msg
|
||||
|
||||
labelIndexes := dns.Split(fqdn)
|
||||
for _, index := range labelIndexes {
|
||||
domain := fqdn[index:]
|
||||
|
||||
in, err = dnsQuery(domain, dns.TypeSOA, nameservers, true)
|
||||
r, err = dnsQuery(domain, dns.TypeSOA, nameservers, true)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if in == nil {
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch in.Rcode {
|
||||
switch r.Rcode {
|
||||
case dns.RcodeSuccess:
|
||||
// Check if we got a SOA RR in the answer section
|
||||
if len(in.Answer) == 0 {
|
||||
if len(r.Answer) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// CNAME records cannot/should not exist at the root of a zone.
|
||||
// So we skip a domain when a CNAME is found.
|
||||
if dnsMsgContainsCNAME(in) {
|
||||
if dnsMsgContainsCNAME(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ans := range in.Answer {
|
||||
for _, ans := range r.Answer {
|
||||
if soa, ok := ans.(*dns.SOA); ok {
|
||||
return newSoaCacheEntry(soa), nil
|
||||
}
|
||||
@ -208,36 +209,46 @@ func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
|
||||
// NXDOMAIN
|
||||
default:
|
||||
// Any response code other than NOERROR and NXDOMAIN is treated as error
|
||||
return nil, fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain)
|
||||
return nil, &DNSError{Message: fmt.Sprintf("unexpected response for '%s'", domain), MsgOut: r}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err))
|
||||
return nil, &DNSError{Message: fmt.Sprintf("could not find the start of authority for '%s'", fqdn), MsgOut: r, Err: err}
|
||||
}
|
||||
|
||||
// dnsMsgContainsCNAME checks for a CNAME answer in msg.
|
||||
func dnsMsgContainsCNAME(msg *dns.Msg) bool {
|
||||
for _, ans := range msg.Answer {
|
||||
if _, ok := ans.(*dns.CNAME); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.ContainsFunc(msg.Answer, func(rr dns.RR) bool {
|
||||
_, ok := rr.(*dns.CNAME)
|
||||
return ok
|
||||
})
|
||||
}
|
||||
|
||||
func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (*dns.Msg, error) {
|
||||
m := createDNSMsg(fqdn, rtype, recursive)
|
||||
|
||||
var in *dns.Msg
|
||||
if len(nameservers) == 0 {
|
||||
return nil, &DNSError{Message: "empty list of nameservers"}
|
||||
}
|
||||
|
||||
var r *dns.Msg
|
||||
var err error
|
||||
var errAll error
|
||||
|
||||
for _, ns := range nameservers {
|
||||
in, err = sendDNSQuery(m, ns)
|
||||
if err == nil && len(in.Answer) > 0 {
|
||||
r, err = sendDNSQuery(m, ns)
|
||||
if err == nil && len(r.Answer) > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
errAll = errors.Join(errAll, err)
|
||||
}
|
||||
return in, err
|
||||
|
||||
if err != nil {
|
||||
return r, errAll
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg {
|
||||
@ -253,32 +264,84 @@ func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg {
|
||||
}
|
||||
|
||||
func sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) {
|
||||
udp := &dns.Client{Net: "udp", Timeout: dnsTimeout}
|
||||
in, _, err := udp.Exchange(m, ns)
|
||||
|
||||
if in != nil && in.Truncated {
|
||||
if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_DNS_TCP_ONLY")); ok {
|
||||
tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout}
|
||||
// If the TCP request succeeds, the err will reset to nil
|
||||
in, _, err = tcp.Exchange(m, ns)
|
||||
r, _, err := tcp.Exchange(m, ns)
|
||||
if err != nil {
|
||||
return r, &DNSError{Message: "DNS call error", MsgIn: m, NS: ns, Err: err}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
return in, err
|
||||
}
|
||||
udp := &dns.Client{Net: "udp", Timeout: dnsTimeout}
|
||||
r, _, err := udp.Exchange(m, ns)
|
||||
|
||||
func formatDNSError(msg *dns.Msg, err error) string {
|
||||
var parts []string
|
||||
|
||||
if msg != nil {
|
||||
parts = append(parts, dns.RcodeToString[msg.Rcode])
|
||||
if r != nil && r.Truncated {
|
||||
tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout}
|
||||
// If the TCP request succeeds, the "err" will reset to nil
|
||||
r, _, err = tcp.Exchange(m, ns)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
parts = append(parts, err.Error())
|
||||
return r, &DNSError{Message: "DNS call error", MsgIn: m, NS: ns, Err: err}
|
||||
}
|
||||
|
||||
if len(parts) > 0 {
|
||||
return ": " + strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
return ""
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// DNSError error related to DNS calls.
|
||||
type DNSError struct {
|
||||
Message string
|
||||
NS string
|
||||
MsgIn *dns.Msg
|
||||
MsgOut *dns.Msg
|
||||
Err error
|
||||
}
|
||||
|
||||
func (d *DNSError) Error() string {
|
||||
var details []string
|
||||
if d.NS != "" {
|
||||
details = append(details, "ns="+d.NS)
|
||||
}
|
||||
|
||||
if d.MsgIn != nil && len(d.MsgIn.Question) > 0 {
|
||||
details = append(details, fmt.Sprintf("question='%s'", formatQuestions(d.MsgIn.Question)))
|
||||
}
|
||||
|
||||
if d.MsgOut != nil {
|
||||
if d.MsgIn == nil || len(d.MsgIn.Question) == 0 {
|
||||
details = append(details, fmt.Sprintf("question='%s'", formatQuestions(d.MsgOut.Question)))
|
||||
}
|
||||
|
||||
details = append(details, "code="+dns.RcodeToString[d.MsgOut.Rcode])
|
||||
}
|
||||
|
||||
msg := "DNS error"
|
||||
if d.Message != "" {
|
||||
msg = d.Message
|
||||
}
|
||||
|
||||
if d.Err != nil {
|
||||
msg += ": " + d.Err.Error()
|
||||
}
|
||||
|
||||
if len(details) > 0 {
|
||||
msg += " [" + strings.Join(details, ", ") + "]"
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func (d *DNSError) Unwrap() error {
|
||||
return d.Err
|
||||
}
|
||||
|
||||
func formatQuestions(questions []dns.Question) string {
|
||||
var parts []string
|
||||
for _, question := range questions {
|
||||
parts = append(parts, strings.ReplaceAll(strings.TrimPrefix(question.String(), ";"), "\t", " "))
|
||||
}
|
||||
|
||||
return strings.Join(parts, ";")
|
||||
}
|
||||
|
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver_unix.go
generated
vendored
Normal file
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver_unix.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !windows
|
||||
|
||||
package dns01
|
||||
|
||||
import "time"
|
||||
|
||||
// dnsTimeout is used to override the default DNS timeout of 10 seconds.
|
||||
var dnsTimeout = 10 * time.Second
|
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver_windows.go
generated
vendored
Normal file
8
vendor/github.com/go-acme/lego/v4/challenge/dns01/nameserver_windows.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build windows
|
||||
|
||||
package dns01
|
||||
|
||||
import "time"
|
||||
|
||||
// dnsTimeout is used to override the default DNS timeout of 20 seconds.
|
||||
var dnsTimeout = 20 * time.Second
|
13
vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go
generated
vendored
13
vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go
generated
vendored
@ -23,14 +23,17 @@ import (
|
||||
// RFC7239 has standardized the different forwarding headers into a single header named Forwarded.
|
||||
// The header value has a different format, so you should use forwardedMatcher
|
||||
// when the http01.ProviderServer operates behind a RFC7239 compatible proxy.
|
||||
// https://tools.ietf.org/html/rfc7239
|
||||
// https://www.rfc-editor.org/rfc/rfc7239.html
|
||||
//
|
||||
// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1),
|
||||
// meaning that
|
||||
// X-Header: a
|
||||
// X-Header: b
|
||||
//
|
||||
// X-Header: a
|
||||
// X-Header: b
|
||||
//
|
||||
// is equal to
|
||||
// X-Header: a, b
|
||||
//
|
||||
// X-Header: a, b
|
||||
//
|
||||
// All matcher implementations (explicitly not excluding arbitraryMatcher!)
|
||||
// have in common that they only match against the first value in such lists.
|
||||
@ -66,7 +69,7 @@ func (m arbitraryMatcher) matches(r *http.Request, domain string) bool {
|
||||
}
|
||||
|
||||
// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name.
|
||||
// See https://tools.ietf.org/html/rfc7239 for details.
|
||||
// See https://www.rfc-editor.org/rfc/rfc7239.html for details.
|
||||
type forwardedMatcher struct{}
|
||||
|
||||
func (m *forwardedMatcher) name() string {
|
||||
|
31
vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go
generated
vendored
31
vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go
generated
vendored
@ -2,9 +2,11 @@ package http01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
@ -14,8 +16,11 @@ import (
|
||||
// It may be instantiated without using the NewProviderServer function if
|
||||
// you want only to use the default values.
|
||||
type ProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
address string
|
||||
network string // must be valid argument to net.Listen
|
||||
|
||||
socketMode fs.FileMode
|
||||
|
||||
matcher domainMatcher
|
||||
done chan bool
|
||||
listener net.Listener
|
||||
@ -29,24 +34,34 @@ func NewProviderServer(iface, port string) *ProviderServer {
|
||||
port = "80"
|
||||
}
|
||||
|
||||
return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}}
|
||||
return &ProviderServer{network: "tcp", address: net.JoinHostPort(iface, port), matcher: &hostMatcher{}}
|
||||
}
|
||||
|
||||
func NewUnixProviderServer(socketPath string, mode fs.FileMode) *ProviderServer {
|
||||
return &ProviderServer{network: "unix", address: socketPath, socketMode: mode, matcher: &hostMatcher{}}
|
||||
}
|
||||
|
||||
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
|
||||
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
var err error
|
||||
s.listener, err = net.Listen("tcp", s.GetAddress())
|
||||
s.listener, err = net.Listen(s.network, s.GetAddress())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTP server for challenge: %w", err)
|
||||
}
|
||||
|
||||
if s.network == "unix" {
|
||||
if err = os.Chmod(s.address, s.socketMode); err != nil {
|
||||
return fmt.Errorf("chmod %s: %w", s.address, err)
|
||||
}
|
||||
}
|
||||
|
||||
s.done = make(chan bool)
|
||||
go s.serve(domain, token, keyAuth)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return net.JoinHostPort(s.iface, s.port)
|
||||
return s.address
|
||||
}
|
||||
|
||||
// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`.
|
||||
@ -69,7 +84,7 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||
//
|
||||
// The exact behavior depends on the value of headerName:
|
||||
// - "" (the empty string) and "Host" will restore the default and only check the Host header
|
||||
// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239
|
||||
// - "Forwarded" will look for a Forwarded header, and inspect it according to https://www.rfc-editor.org/rfc/rfc7239.html
|
||||
// - any other value will check the header value with the same name.
|
||||
func (s *ProviderServer) SetProxyHeader(headerName string) {
|
||||
switch h := textproto.CanonicalMIMEHeaderKey(headerName); h {
|
||||
@ -85,7 +100,7 @@ func (s *ProviderServer) SetProxyHeader(headerName string) {
|
||||
func (s *ProviderServer) serve(domain, token, keyAuth string) {
|
||||
path := ChallengePath(token)
|
||||
|
||||
// The incoming request must will be validated to prevent DNS rebind attacks.
|
||||
// The incoming request will be validated to prevent DNS rebind attacks.
|
||||
// We only respond with the keyAuth, when we're receiving a GET requests with
|
||||
// the "Host" header matching the domain (the latter is configurable though SetProxyHeader).
|
||||
mux := http.NewServeMux()
|
||||
@ -99,7 +114,7 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) {
|
||||
}
|
||||
log.Infof("[%s] Served key authentication", domain)
|
||||
} else {
|
||||
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
|
||||
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
|
||||
_, err := w.Write([]byte("TEST"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
2
vendor/github.com/go-acme/lego/v4/challenge/resolver/errors.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/challenge/resolver/errors.go
generated
vendored
@ -19,7 +19,7 @@ func (e obtainError) Error() string {
|
||||
sort.Strings(domains)
|
||||
|
||||
for _, domain := range domains {
|
||||
buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain]))
|
||||
_, _ = fmt.Fprintf(buffer, "[%s] %s\n", domain, e[domain])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
2
vendor/github.com/go-acme/lego/v4/challenge/resolver/prober.go
generated
vendored
2
vendor/github.com/go-acme/lego/v4/challenge/resolver/prober.go
generated
vendored
@ -128,7 +128,7 @@ func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
|
||||
}
|
||||
|
||||
func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
|
||||
// For all valid preSolvers, first submit the challenges so they have max time to propagate
|
||||
// For all valid preSolvers, first submit the challenges, so they have max time to propagate
|
||||
for _, authSolver := range authSolvers {
|
||||
authz := authSolver.authz
|
||||
if solvr, ok := authSolver.solver.(preSolver); ok {
|
||||
|
13
vendor/github.com/go-acme/lego/v4/challenge/resolver/solver_manager.go
generated
vendored
13
vendor/github.com/go-acme/lego/v4/challenge/resolver/solver_manager.go
generated
vendored
@ -1,7 +1,6 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
@ -54,7 +53,7 @@ func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.Cha
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove Remove a challenge type from the available solvers.
|
||||
// Remove removes a challenge type from the available solvers.
|
||||
func (c *SolverManager) Remove(chlgType challenge.Type) {
|
||||
delete(c.solvers, chlgType)
|
||||
}
|
||||
@ -107,21 +106,17 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error {
|
||||
bo.MaxInterval = 10 * initialInterval
|
||||
bo.MaxElapsedTime = 100 * initialInterval
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// After the path is sent, the ACME server will access our server.
|
||||
// Repeatedly check the server for an updated status on our request.
|
||||
operation := func() error {
|
||||
authz, err := core.Authorizations.Get(chlng.AuthorizationURL)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
|
||||
valid, err := checkAuthorizationStatus(authz)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
|
||||
if valid {
|
||||
@ -132,7 +127,7 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error {
|
||||
return errors.New("the server didn't respond to our request")
|
||||
}
|
||||
|
||||
return backoff.Retry(operation, backoff.WithContext(bo, ctx))
|
||||
return backoff.Retry(operation, bo)
|
||||
}
|
||||
|
||||
func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) {
|
||||
|
4
vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go
generated
vendored
4
vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go
generated
vendored
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.1
|
||||
// Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-6.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
|
||||
|
||||
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
|
||||
@ -83,7 +83,7 @@ func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
||||
|
||||
// Add the keyAuth digest as the acmeValidation-v1 extension
|
||||
// (marked as critical such that it won't be used by non-ACME software).
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-3
|
||||
// Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-3
|
||||
extensions := []pkix.Extension{
|
||||
{
|
||||
Id: idPeAcmeIdentifierV1,
|
||||
|
4
vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go
generated
vendored
4
vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go
generated
vendored
@ -40,7 +40,7 @@ func (s *ProviderServer) GetAddress() string {
|
||||
return net.JoinHostPort(s.iface, s.port)
|
||||
}
|
||||
|
||||
// Present generates a certificate with a SHA-256 digest of the keyAuth provided
|
||||
// Present generates a certificate with an SHA-256 digest of the keyAuth provided
|
||||
// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec.
|
||||
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if s.port == "" {
|
||||
@ -61,7 +61,7 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
|
||||
// We must set that the `acme-tls/1` application level protocol is supported
|
||||
// so that the protocol negotiation can succeed. Reference:
|
||||
// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2
|
||||
// https://www.rfc-editor.org/rfc/rfc8737.html#section-6.2
|
||||
tlsConf.NextProtos = []string{ACMETLS1Protocol}
|
||||
|
||||
// Create the listener with the created tls.Config.
|
||||
|
55
vendor/github.com/go-acme/lego/v4/lego/client_config.go
generated
vendored
55
vendor/github.com/go-acme/lego/v4/lego/client_config.go
generated
vendored
@ -4,10 +4,11 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
@ -17,13 +18,18 @@ import (
|
||||
const (
|
||||
// caCertificatesEnvVar is the environment variable name that can be used to
|
||||
// specify the path to PEM encoded CA Certificates that can be used to
|
||||
// authenticate an ACME server with a HTTPS certificate not issued by a CA in
|
||||
// authenticate an ACME server with an HTTPS certificate not issued by a CA in
|
||||
// the system-wide trusted root list.
|
||||
// Multiple file paths can be added by using os.PathListSeparator as a separator.
|
||||
caCertificatesEnvVar = "LEGO_CA_CERTIFICATES"
|
||||
|
||||
// caSystemCertPool is the environment variable name that can be used to define
|
||||
// if the certificates pool must use a copy of the system cert pool.
|
||||
caSystemCertPool = "LEGO_CA_SYSTEM_CERT_POOL"
|
||||
|
||||
// caServerNameEnvVar is the environment variable name that can be used to
|
||||
// specify the CA server name that can be used to
|
||||
// authenticate an ACME server with a HTTPS certificate not issued by a CA in
|
||||
// authenticate an ACME server with an HTTPS certificate not issued by a CA in
|
||||
// the system-wide trusted root list.
|
||||
caServerNameEnvVar = "LEGO_CA_SERVER_NAME"
|
||||
|
||||
@ -82,23 +88,44 @@ func createDefaultHTTPClient() *http.Client {
|
||||
}
|
||||
|
||||
// initCertPool creates a *x509.CertPool populated with the PEM certificates
|
||||
// found in the filepath specified in the caCertificatesEnvVar OS environment
|
||||
// variable. If the caCertificatesEnvVar is not set then initCertPool will
|
||||
// return nil. If there is an error creating a *x509.CertPool from the provided
|
||||
// caCertificatesEnvVar value then initCertPool will panic.
|
||||
// found in the filepath specified in the caCertificatesEnvVar OS environment variable.
|
||||
// If the caCertificatesEnvVar is not set then initCertPool will return nil.
|
||||
// If there is an error creating a *x509.CertPool from the provided caCertificatesEnvVar value then initCertPool will panic.
|
||||
// If the caSystemCertPool is set to a "truthy value" (`1`, `t`, `T`, `TRUE`, `true`, `True`) then a copy of system cert pool will be used.
|
||||
// caSystemCertPool requires caCertificatesEnvVar to be set.
|
||||
func initCertPool() *x509.CertPool {
|
||||
if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" {
|
||||
customCAs, err := ioutil.ReadFile(customCACertsPath)
|
||||
customCACertsPath := os.Getenv(caCertificatesEnvVar)
|
||||
if customCACertsPath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPool := getCertPool()
|
||||
|
||||
for _, customPath := range strings.Split(customCACertsPath, string(os.PathListSeparator)) {
|
||||
customCAs, err := os.ReadFile(customPath)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error reading %s=%q: %v",
|
||||
caCertificatesEnvVar, customCACertsPath, err))
|
||||
caCertificatesEnvVar, customPath, err))
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
|
||||
if ok := certPool.AppendCertsFromPEM(customCAs); !ok {
|
||||
panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v",
|
||||
caCertificatesEnvVar, customCACertsPath, err))
|
||||
caCertificatesEnvVar, customPath, err))
|
||||
}
|
||||
return certPool
|
||||
}
|
||||
return nil
|
||||
|
||||
return certPool
|
||||
}
|
||||
|
||||
func getCertPool() *x509.CertPool {
|
||||
useSystemCertPool, _ := strconv.ParseBool(os.Getenv(caSystemCertPool))
|
||||
if !useSystemCertPool {
|
||||
return x509.NewCertPool()
|
||||
}
|
||||
|
||||
pool, err := x509.SystemCertPool()
|
||||
if err == nil {
|
||||
return pool
|
||||
}
|
||||
return x509.NewCertPool()
|
||||
}
|
||||
|
95
vendor/github.com/go-acme/lego/v4/platform/config/env/env.go
generated
vendored
95
vendor/github.com/go-acme/lego/v4/platform/config/env/env.go
generated
vendored
@ -3,7 +3,6 @@ package env
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -55,7 +54,6 @@ func Get(names ...string) (map[string]string, error) {
|
||||
// // LEGO_TWO=""
|
||||
// env.GetWithFallback([]string{"LEGO_ONE", "LEGO_TWO"})
|
||||
// // => error
|
||||
//
|
||||
func GetWithFallback(groups ...[]string) (map[string]string, error) {
|
||||
values := map[string]string{}
|
||||
|
||||
@ -80,15 +78,26 @@ func GetWithFallback(groups ...[]string) (map[string]string, error) {
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func GetOneWithFallback[T any](main string, defaultValue T, fn func(string) (T, error), names ...string) T {
|
||||
v, _ := getOneWithFallback(main, names...)
|
||||
|
||||
value, err := fn(v)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func getOneWithFallback(main string, names ...string) (string, string) {
|
||||
value := GetOrFile(main)
|
||||
if len(value) > 0 {
|
||||
if value != "" {
|
||||
return value, main
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
value := GetOrFile(name)
|
||||
if len(value) > 0 {
|
||||
if value != "" {
|
||||
return value, main
|
||||
}
|
||||
}
|
||||
@ -96,43 +105,32 @@ func getOneWithFallback(main string, names ...string) (string, string) {
|
||||
return "", main
|
||||
}
|
||||
|
||||
// GetOrDefaultInt returns the given environment variable value as an integer.
|
||||
// Returns the default if the envvar cannot be coopered to an int, or is not found.
|
||||
func GetOrDefaultInt(envVar string, defaultValue int) int {
|
||||
v, err := strconv.Atoi(GetOrFile(envVar))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// GetOrDefaultSecond returns the given environment variable value as an time.Duration (second).
|
||||
// Returns the default if the envvar cannot be coopered to an int, or is not found.
|
||||
func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration {
|
||||
v := GetOrDefaultInt(envVar, -1)
|
||||
if v < 0 {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return time.Duration(v) * time.Second
|
||||
}
|
||||
|
||||
// GetOrDefaultString returns the given environment variable value as a string.
|
||||
// Returns the default if the envvar cannot be find.
|
||||
func GetOrDefaultString(envVar, defaultValue string) string {
|
||||
v := GetOrFile(envVar)
|
||||
if v == "" {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return v
|
||||
// Returns the default if the env var cannot be found.
|
||||
func GetOrDefaultString(envVar string, defaultValue string) string {
|
||||
return getOrDefault(envVar, defaultValue, ParseString)
|
||||
}
|
||||
|
||||
// GetOrDefaultBool returns the given environment variable value as a boolean.
|
||||
// Returns the default if the envvar cannot be coopered to a boolean, or is not found.
|
||||
// Returns the default if the env var cannot be coopered to a boolean, or is not found.
|
||||
func GetOrDefaultBool(envVar string, defaultValue bool) bool {
|
||||
v, err := strconv.ParseBool(GetOrFile(envVar))
|
||||
return getOrDefault(envVar, defaultValue, strconv.ParseBool)
|
||||
}
|
||||
|
||||
// GetOrDefaultInt returns the given environment variable value as an integer.
|
||||
// Returns the default if the env var cannot be coopered to an int, or is not found.
|
||||
func GetOrDefaultInt(envVar string, defaultValue int) int {
|
||||
return getOrDefault(envVar, defaultValue, strconv.Atoi)
|
||||
}
|
||||
|
||||
// GetOrDefaultSecond returns the given environment variable value as a time.Duration (second).
|
||||
// Returns the default if the env var cannot be coopered to an int, or is not found.
|
||||
func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration {
|
||||
return getOrDefault(envVar, defaultValue, ParseSecond)
|
||||
}
|
||||
|
||||
func getOrDefault[T any](envVar string, defaultValue T, fn func(string) (T, error)) T {
|
||||
v, err := fn(GetOrFile(envVar))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
@ -155,7 +153,7 @@ func GetOrFile(envVar string) string {
|
||||
return envVarValue
|
||||
}
|
||||
|
||||
fileContents, err := ioutil.ReadFile(fileVarValue)
|
||||
fileContents, err := os.ReadFile(fileVarValue)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, fileVar, err)
|
||||
return ""
|
||||
@ -163,3 +161,26 @@ func GetOrFile(envVar string) string {
|
||||
|
||||
return strings.TrimSuffix(string(fileContents), "\n")
|
||||
}
|
||||
|
||||
// ParseSecond parses env var value (string) to a second (time.Duration).
|
||||
func ParseSecond(s string) (time.Duration, error) {
|
||||
v, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
return 0, fmt.Errorf("unsupported value: %d", v)
|
||||
}
|
||||
|
||||
return time.Duration(v) * time.Second, nil
|
||||
}
|
||||
|
||||
// ParseString parses env var value (string) to a string but throws an error when the string is empty.
|
||||
func ParseString(s string) (string, error) {
|
||||
if s == "" {
|
||||
return "", errors.New("empty string")
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
5
vendor/github.com/go-acme/lego/v4/platform/wait/wait.go
generated
vendored
5
vendor/github.com/go-acme/lego/v4/platform/wait/wait.go
generated
vendored
@ -1,7 +1,6 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -18,9 +17,9 @@ func For(msg string, timeout, interval time.Duration, f func() (bool, error)) er
|
||||
select {
|
||||
case <-timeUp:
|
||||
if lastErr == nil {
|
||||
return errors.New("time limit exceeded")
|
||||
return fmt.Errorf("%s: time limit exceeded", msg)
|
||||
}
|
||||
return fmt.Errorf("time limit exceeded: last error: %w", lastErr)
|
||||
return fmt.Errorf("%s: time limit exceeded: last error: %w", msg, lastErr)
|
||||
default:
|
||||
}
|
||||
|
||||
|
133
vendor/github.com/go-acme/lego/v4/providers/dns/internal/errutils/client.go
generated
vendored
Normal file
133
vendor/github.com/go-acme/lego/v4/providers/dns/internal/errutils/client.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
package errutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const legoDebugClientVerboseError = "LEGO_DEBUG_CLIENT_VERBOSE_ERROR"
|
||||
|
||||
// HTTPDoError uses with `(http.Client).Do` error.
|
||||
type HTTPDoError struct {
|
||||
req *http.Request
|
||||
err error
|
||||
}
|
||||
|
||||
// NewHTTPDoError creates a new HTTPDoError.
|
||||
func NewHTTPDoError(req *http.Request, err error) *HTTPDoError {
|
||||
return &HTTPDoError{req: req, err: err}
|
||||
}
|
||||
|
||||
func (h HTTPDoError) Error() string {
|
||||
msg := "unable to communicate with the API server:"
|
||||
|
||||
if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
|
||||
msg += fmt.Sprintf(" [request: %s %s]", h.req.Method, h.req.URL)
|
||||
}
|
||||
|
||||
if h.err == nil {
|
||||
return msg
|
||||
}
|
||||
|
||||
return msg + fmt.Sprintf(" error: %v", h.err)
|
||||
}
|
||||
|
||||
func (h HTTPDoError) Unwrap() error {
|
||||
return h.err
|
||||
}
|
||||
|
||||
// ReadResponseError use with `io.ReadAll` when reading response body.
|
||||
type ReadResponseError struct {
|
||||
req *http.Request
|
||||
StatusCode int
|
||||
err error
|
||||
}
|
||||
|
||||
// NewReadResponseError creates a new ReadResponseError.
|
||||
func NewReadResponseError(req *http.Request, statusCode int, err error) *ReadResponseError {
|
||||
return &ReadResponseError{req: req, StatusCode: statusCode, err: err}
|
||||
}
|
||||
|
||||
func (r ReadResponseError) Error() string {
|
||||
msg := "unable to read response body:"
|
||||
|
||||
if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
|
||||
msg += fmt.Sprintf(" [request: %s %s]", r.req.Method, r.req.URL)
|
||||
}
|
||||
|
||||
msg += fmt.Sprintf(" [status code: %d]", r.StatusCode)
|
||||
|
||||
if r.err == nil {
|
||||
return msg
|
||||
}
|
||||
|
||||
return msg + fmt.Sprintf(" error: %v", r.err)
|
||||
}
|
||||
|
||||
func (r ReadResponseError) Unwrap() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// UnmarshalError uses with `json.Unmarshal` or `xml.Unmarshal` when reading response body.
|
||||
type UnmarshalError struct {
|
||||
req *http.Request
|
||||
StatusCode int
|
||||
Body []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// NewUnmarshalError creates a new UnmarshalError.
|
||||
func NewUnmarshalError(req *http.Request, statusCode int, body []byte, err error) *UnmarshalError {
|
||||
return &UnmarshalError{req: req, StatusCode: statusCode, Body: bytes.TrimSpace(body), err: err}
|
||||
}
|
||||
|
||||
func (u UnmarshalError) Error() string {
|
||||
msg := "unable to unmarshal response:"
|
||||
|
||||
if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
|
||||
msg += fmt.Sprintf(" [request: %s %s]", u.req.Method, u.req.URL)
|
||||
}
|
||||
|
||||
msg += fmt.Sprintf(" [status code: %d] body: %s", u.StatusCode, string(u.Body))
|
||||
|
||||
if u.err == nil {
|
||||
return msg
|
||||
}
|
||||
|
||||
return msg + fmt.Sprintf(" error: %v", u.err)
|
||||
}
|
||||
|
||||
func (u UnmarshalError) Unwrap() error {
|
||||
return u.err
|
||||
}
|
||||
|
||||
// UnexpectedStatusCodeError use when the status of the response is unexpected but there is no API error type.
|
||||
type UnexpectedStatusCodeError struct {
|
||||
req *http.Request
|
||||
StatusCode int
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// NewUnexpectedStatusCodeError creates a new UnexpectedStatusCodeError.
|
||||
func NewUnexpectedStatusCodeError(req *http.Request, statusCode int, body []byte) *UnexpectedStatusCodeError {
|
||||
return &UnexpectedStatusCodeError{req: req, StatusCode: statusCode, Body: bytes.TrimSpace(body)}
|
||||
}
|
||||
|
||||
func NewUnexpectedResponseStatusCodeError(req *http.Request, resp *http.Response) *UnexpectedStatusCodeError {
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
return &UnexpectedStatusCodeError{req: req, StatusCode: resp.StatusCode, Body: bytes.TrimSpace(raw)}
|
||||
}
|
||||
|
||||
func (u UnexpectedStatusCodeError) Error() string {
|
||||
msg := "unexpected status code:"
|
||||
|
||||
if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
|
||||
msg += fmt.Sprintf(" [request: %s %s]", u.req.Method, u.req.URL)
|
||||
}
|
||||
|
||||
return msg + fmt.Sprintf(" [status code: %d] body: %s", u.StatusCode, string(u.Body))
|
||||
}
|
179
vendor/github.com/go-acme/lego/v4/providers/dns/ovh/ovh.go
generated
vendored
179
vendor/github.com/go-acme/lego/v4/providers/dns/ovh/ovh.go
generated
vendored
@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -15,16 +14,14 @@ import (
|
||||
)
|
||||
|
||||
// OVH API reference: https://eu.api.ovh.com/
|
||||
// Create a Token: https://eu.api.ovh.com/createToken/
|
||||
// Create a Token: https://eu.api.ovh.com/createToken/
|
||||
// Create a OAuth2 client: https://eu.api.ovh.com/console-preview/?section=%2Fme&branch=v1#post-/me/api/oauth2/client
|
||||
|
||||
// Environment variables names.
|
||||
const (
|
||||
envNamespace = "OVH_"
|
||||
|
||||
EnvEndpoint = envNamespace + "ENDPOINT"
|
||||
EnvApplicationKey = envNamespace + "APPLICATION_KEY"
|
||||
EnvApplicationSecret = envNamespace + "APPLICATION_SECRET"
|
||||
EnvConsumerKey = envNamespace + "CONSUMER_KEY"
|
||||
EnvEndpoint = envNamespace + "ENDPOINT"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
@ -32,6 +29,19 @@ const (
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
// Authenticate using application key.
|
||||
const (
|
||||
EnvApplicationKey = envNamespace + "APPLICATION_KEY"
|
||||
EnvApplicationSecret = envNamespace + "APPLICATION_SECRET"
|
||||
EnvConsumerKey = envNamespace + "CONSUMER_KEY"
|
||||
)
|
||||
|
||||
// Authenticate using OAuth2 client.
|
||||
const (
|
||||
EnvClientID = envNamespace + "CLIENT_ID"
|
||||
EnvClientSecret = envNamespace + "CLIENT_SECRET"
|
||||
)
|
||||
|
||||
// Record a DNS record.
|
||||
type Record struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
@ -42,18 +52,32 @@ type Record struct {
|
||||
Zone string `json:"zone,omitempty"`
|
||||
}
|
||||
|
||||
// OAuth2Config the OAuth2 specific configuration.
|
||||
type OAuth2Config struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
APIEndpoint string
|
||||
ApplicationKey string
|
||||
ApplicationSecret string
|
||||
ConsumerKey string
|
||||
APIEndpoint string
|
||||
|
||||
ApplicationKey string
|
||||
ApplicationSecret string
|
||||
ConsumerKey string
|
||||
|
||||
OAuth2Config *OAuth2Config
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func (c *Config) hasAppKeyAuth() bool {
|
||||
return c.ApplicationKey != "" || c.ApplicationSecret != "" || c.ConsumerKey != ""
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
@ -78,17 +102,11 @@ type DNSProvider struct {
|
||||
// Credentials must be passed in the environment variables:
|
||||
// OVH_ENDPOINT (must be either "ovh-eu" or "ovh-ca"), OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvEndpoint, EnvApplicationKey, EnvApplicationSecret, EnvConsumerKey)
|
||||
config, err := createConfigFromEnvVars()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ovh: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIEndpoint = values[EnvEndpoint]
|
||||
config.ApplicationKey = values[EnvApplicationKey]
|
||||
config.ApplicationSecret = values[EnvApplicationSecret]
|
||||
config.ConsumerKey = values[EnvConsumerKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
@ -98,16 +116,11 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
return nil, errors.New("ovh: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.APIEndpoint == "" || config.ApplicationKey == "" || config.ApplicationSecret == "" || config.ConsumerKey == "" {
|
||||
return nil, errors.New("ovh: credentials missing")
|
||||
if config.OAuth2Config != nil && config.hasAppKeyAuth() {
|
||||
return nil, errors.New("ovh: can't use both authentication systems (ApplicationKey and OAuth2)")
|
||||
}
|
||||
|
||||
client, err := ovh.NewClient(
|
||||
config.APIEndpoint,
|
||||
config.ApplicationKey,
|
||||
config.ApplicationSecret,
|
||||
config.ConsumerKey,
|
||||
)
|
||||
client, err := newClient(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ovh: %w", err)
|
||||
}
|
||||
@ -123,19 +136,23 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
// Parse domain name
|
||||
authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ovh: could not determine zone for domain %q: %w", domain, err)
|
||||
return fmt.Errorf("ovh: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
authZone = dns01.UnFqdn(authZone)
|
||||
subDomain := extractRecordName(fqdn, authZone)
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ovh: %w", err)
|
||||
}
|
||||
|
||||
reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone)
|
||||
reqData := Record{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: d.config.TTL}
|
||||
reqData := Record{FieldType: "TXT", SubDomain: subDomain, Target: info.Value, TTL: d.config.TTL}
|
||||
|
||||
// Create TXT record
|
||||
var respData Record
|
||||
@ -160,19 +177,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _ := dns01.GetRecord(domain, keyAuth)
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
// get the record's unique ID from when we created it
|
||||
d.recordIDsMu.Lock()
|
||||
recordID, ok := d.recordIDs[token]
|
||||
d.recordIDsMu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("ovh: unknown record ID for '%s'", fqdn)
|
||||
return fmt.Errorf("ovh: unknown record ID for '%s'", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ovh: could not determine zone for domain %q: %w", domain, err)
|
||||
return fmt.Errorf("ovh: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
authZone = dns01.UnFqdn(authZone)
|
||||
@ -205,10 +222,94 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func extractRecordName(fqdn, zone string) string {
|
||||
name := dns01.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+zone); idx != -1 {
|
||||
return name[:idx]
|
||||
func createConfigFromEnvVars() (*Config, error) {
|
||||
firstAppKeyEnvVar := findFirstValuedEnvVar(EnvApplicationKey, EnvApplicationSecret, EnvConsumerKey)
|
||||
firstOAuth2EnvVar := findFirstValuedEnvVar(EnvClientID, EnvClientSecret)
|
||||
|
||||
if firstAppKeyEnvVar != "" && firstOAuth2EnvVar != "" {
|
||||
return nil, fmt.Errorf("can't use both %s and %s at the same time", firstAppKeyEnvVar, firstOAuth2EnvVar)
|
||||
}
|
||||
return name
|
||||
|
||||
config := NewDefaultConfig()
|
||||
|
||||
if firstOAuth2EnvVar != "" {
|
||||
values, err := env.Get(EnvEndpoint, EnvClientID, EnvClientSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.APIEndpoint = values[EnvEndpoint]
|
||||
config.OAuth2Config = &OAuth2Config{
|
||||
ClientID: values[EnvClientID],
|
||||
ClientSecret: values[EnvClientSecret],
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
values, err := env.Get(EnvEndpoint, EnvApplicationKey, EnvApplicationSecret, EnvConsumerKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.APIEndpoint = values[EnvEndpoint]
|
||||
|
||||
config.ApplicationKey = values[EnvApplicationKey]
|
||||
config.ApplicationSecret = values[EnvApplicationSecret]
|
||||
config.ConsumerKey = values[EnvConsumerKey]
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func findFirstValuedEnvVar(envVars ...string) string {
|
||||
for _, envVar := range envVars {
|
||||
if env.GetOrFile(envVar) != "" {
|
||||
return envVar
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func newClient(config *Config) (*ovh.Client, error) {
|
||||
if config.OAuth2Config == nil {
|
||||
return newClientApplicationKey(config)
|
||||
}
|
||||
|
||||
return newClientOAuth2(config)
|
||||
}
|
||||
|
||||
func newClientApplicationKey(config *Config) (*ovh.Client, error) {
|
||||
if config.APIEndpoint == "" || config.ApplicationKey == "" || config.ApplicationSecret == "" || config.ConsumerKey == "" {
|
||||
return nil, errors.New("credentials are missing")
|
||||
}
|
||||
|
||||
client, err := ovh.NewClient(
|
||||
config.APIEndpoint,
|
||||
config.ApplicationKey,
|
||||
config.ApplicationSecret,
|
||||
config.ConsumerKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new client: %w", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func newClientOAuth2(config *Config) (*ovh.Client, error) {
|
||||
if config.APIEndpoint == "" || config.OAuth2Config.ClientID == "" || config.OAuth2Config.ClientSecret == "" {
|
||||
return nil, errors.New("credentials are missing")
|
||||
}
|
||||
|
||||
client, err := ovh.NewOAuth2Client(
|
||||
config.APIEndpoint,
|
||||
config.OAuth2Config.ClientID,
|
||||
config.OAuth2Config.ClientSecret,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new OAuth2 client: %w", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
37
vendor/github.com/go-acme/lego/v4/providers/dns/ovh/ovh.toml
generated
vendored
37
vendor/github.com/go-acme/lego/v4/providers/dns/ovh/ovh.toml
generated
vendored
@ -5,11 +5,20 @@ Code = "ovh"
|
||||
Since = "v0.4.0"
|
||||
|
||||
Example = '''
|
||||
# Application Key authentication:
|
||||
|
||||
OVH_APPLICATION_KEY=1234567898765432 \
|
||||
OVH_APPLICATION_SECRET=b9841238feb177a84330febba8a832089 \
|
||||
OVH_CONSUMER_KEY=256vfsd347245sdfg \
|
||||
OVH_ENDPOINT=ovh-eu \
|
||||
lego --email myemail@example.com --dns ovh --domains my.example.org run
|
||||
lego --email you@example.com --dns ovh --domains my.example.org run
|
||||
|
||||
# Or OAuth2:
|
||||
|
||||
OVH_CLIENT_ID=yyy \
|
||||
OVH_CLIENT_SECRET=xxx \
|
||||
OVH_ENDPOINT=ovh-eu \
|
||||
lego --email you@example.com --dns ovh --domains my.example.org run
|
||||
'''
|
||||
|
||||
Additional = '''
|
||||
@ -17,7 +26,7 @@ Additional = '''
|
||||
|
||||
Application key and secret can be created by following the [OVH guide](https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/).
|
||||
|
||||
When requesting the consumer key, the following configuration can be use to define access rights:
|
||||
When requesting the consumer key, the following configuration can be used to define access rights:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -33,14 +42,32 @@ When requesting the consumer key, the following configuration can be use to defi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## OAuth2 Client Credentials
|
||||
|
||||
Another method for authentication is by using OAuth2 client credentials.
|
||||
|
||||
An IAM policy and service account can be created by following the [OVH guide](https://help.ovhcloud.com/csm/en-manage-service-account?id=kb_article_view&sysparm_article=KB0059343).
|
||||
|
||||
Following IAM policies need to be authorized for the affected domain:
|
||||
|
||||
* dnsZone:apiovh:record/create
|
||||
* dnsZone:apiovh:record/delete
|
||||
* dnsZone:apiovh:refresh
|
||||
|
||||
## Important Note
|
||||
|
||||
Both authentication methods cannot be used at the same time.
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
OVH_ENDPOINT = "Endpoint URL (ovh-eu or ovh-ca)"
|
||||
OVH_APPLICATION_KEY = "Application key"
|
||||
OVH_APPLICATION_SECRET = "Application secret"
|
||||
OVH_CONSUMER_KEY = "Consumer key"
|
||||
OVH_APPLICATION_KEY = "Application key (Application Key authentication)"
|
||||
OVH_APPLICATION_SECRET = "Application secret (Application Key authentication)"
|
||||
OVH_CONSUMER_KEY = "Consumer key (Application Key authentication)"
|
||||
OVH_CLIENT_ID = "Client ID (OAuth2)"
|
||||
OVH_CLIENT_SECRET = "Client secret (OAuth2)"
|
||||
[Configuration.Additional]
|
||||
OVH_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
OVH_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
|
228
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/internal/client.go
generated
vendored
Normal file
228
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/internal/client.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Client the PowerDNS API client.
|
||||
type Client struct {
|
||||
serverName string
|
||||
apiKey string
|
||||
|
||||
apiVersion int
|
||||
|
||||
Host *url.URL
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewClient creates a new Client.
|
||||
func NewClient(host *url.URL, serverName string, apiVersion int, apiKey string) *Client {
|
||||
return &Client{
|
||||
serverName: serverName,
|
||||
apiKey: apiKey,
|
||||
apiVersion: apiVersion,
|
||||
Host: host,
|
||||
HTTPClient: &http.Client{Timeout: 5 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) APIVersion() int {
|
||||
return c.apiVersion
|
||||
}
|
||||
|
||||
func (c *Client) SetAPIVersion(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
c.apiVersion, err = c.getAPIVersion(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) getAPIVersion(ctx context.Context) (int, error) {
|
||||
endpoint := c.joinPath("/", "api")
|
||||
|
||||
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result, err := c.do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var versions []apiVersion
|
||||
err = json.Unmarshal(result, &versions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
latestVersion := 0
|
||||
for _, v := range versions {
|
||||
if v.Version > latestVersion {
|
||||
latestVersion = v.Version
|
||||
}
|
||||
}
|
||||
|
||||
return latestVersion, err
|
||||
}
|
||||
|
||||
func (c *Client) GetHostedZone(ctx context.Context, authZone string) (*HostedZone, error) {
|
||||
endpoint := c.joinPath("/", "servers", c.serverName, "zones", dns.Fqdn(authZone))
|
||||
|
||||
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := c.do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var zone HostedZone
|
||||
err = json.Unmarshal(result, &zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convert pre-v1 API result
|
||||
if len(zone.Records) > 0 {
|
||||
zone.RRSets = []RRSet{}
|
||||
for _, record := range zone.Records {
|
||||
set := RRSet{
|
||||
Name: record.Name,
|
||||
Type: record.Type,
|
||||
Records: []Record{record},
|
||||
}
|
||||
zone.RRSets = append(zone.RRSets, set)
|
||||
}
|
||||
}
|
||||
|
||||
return &zone, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRecords(ctx context.Context, zone *HostedZone, sets RRSets) error {
|
||||
endpoint := c.joinPath("/", "servers", c.serverName, "zones", zone.ID)
|
||||
|
||||
req, err := newJSONRequest(ctx, http.MethodPatch, endpoint, sets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Notify(ctx context.Context, zone *HostedZone) error {
|
||||
if c.apiVersion < 1 || zone.Kind != "Master" && zone.Kind != "Slave" {
|
||||
return nil
|
||||
}
|
||||
|
||||
endpoint := c.joinPath("/", "servers", c.serverName, "zones", zone.ID, "notify")
|
||||
|
||||
req, err := newJSONRequest(ctx, http.MethodPut, endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) joinPath(elem ...string) *url.URL {
|
||||
p := path.Join(elem...)
|
||||
|
||||
if p != "/api" && c.apiVersion > 0 && !strings.HasPrefix(p, "/api/v") {
|
||||
p = path.Join("/api", "v"+strconv.Itoa(c.apiVersion), p)
|
||||
}
|
||||
|
||||
return c.Host.JoinPath(p)
|
||||
}
|
||||
|
||||
func (c *Client) do(req *http.Request) (json.RawMessage, error) {
|
||||
req.Header.Set("X-API-Key", c.apiKey)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errutils.NewHTTPDoError(req, err)
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) {
|
||||
return nil, errutils.NewUnexpectedResponseStatusCodeError(req, resp)
|
||||
}
|
||||
|
||||
var msg json.RawMessage
|
||||
err = json.NewDecoder(resp.Body).Decode(&msg)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
// empty body
|
||||
return nil, nil
|
||||
}
|
||||
// other error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check for PowerDNS error message
|
||||
if len(msg) > 0 && msg[0] == '{' {
|
||||
var errInfo apiError
|
||||
err = json.Unmarshal(msg, &errInfo)
|
||||
if err != nil {
|
||||
return nil, errutils.NewUnmarshalError(req, resp.StatusCode, msg, err)
|
||||
}
|
||||
if errInfo.ShortMsg != "" {
|
||||
return nil, fmt.Errorf("error talking to PDNS API: %w", errInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if payload != nil {
|
||||
err := json.NewEncoder(buf).Encode(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request JSON body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, strings.TrimSuffix(endpoint.String(), "/"), buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
// PowerDNS doesn't follow HTTP convention about the "Content-Type" header.
|
||||
if method != http.MethodGet && method != http.MethodDelete {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
48
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/internal/types.go
generated
vendored
Normal file
48
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/internal/types.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package internal
|
||||
|
||||
type Record struct {
|
||||
Content string `json:"content"`
|
||||
Disabled bool `json:"disabled"`
|
||||
|
||||
// pre-v1 API
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
}
|
||||
|
||||
type HostedZone struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Kind string `json:"kind"`
|
||||
RRSets []RRSet `json:"rrsets"`
|
||||
|
||||
// pre-v1 API
|
||||
Records []Record `json:"records"`
|
||||
}
|
||||
|
||||
type RRSet struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Kind string `json:"kind"`
|
||||
ChangeType string `json:"changetype"`
|
||||
Records []Record `json:"records,omitempty"`
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
}
|
||||
|
||||
type RRSets struct {
|
||||
RRSets []RRSet `json:"rrsets"`
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
ShortMsg string `json:"error"`
|
||||
}
|
||||
|
||||
func (a apiError) Error() string {
|
||||
return a.ShortMsg
|
||||
}
|
||||
|
||||
type apiVersion struct {
|
||||
URL string `json:"url"`
|
||||
Version int `json:"version"`
|
||||
}
|
228
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/pdns.go
generated
vendored
Normal file
228
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/pdns.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
// Package pdns implements a DNS provider for solving the DNS-01 challenge using PowerDNS nameserver.
|
||||
package pdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/go-acme/lego/v4/providers/dns/pdns/internal"
|
||||
)
|
||||
|
||||
// Environment variables names.
|
||||
const (
|
||||
envNamespace = "PDNS_"
|
||||
|
||||
EnvAPIKey = envNamespace + "API_KEY"
|
||||
EnvAPIURL = envNamespace + "API_URL"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvAPIVersion = envNamespace + "API_VERSION"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
EnvServerName = envNamespace + "SERVER_NAME"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
APIKey string
|
||||
Host *url.URL
|
||||
ServerName string
|
||||
APIVersion int
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
ServerName: env.GetOrDefaultString(EnvServerName, "localhost"),
|
||||
APIVersion: env.GetOrDefaultInt(EnvAPIVersion, 0),
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 120*time.Second),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 2*time.Second),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider implements the challenge.Provider interface.
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *internal.Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for pdns.
|
||||
// Credentials must be passed in the environment variable:
|
||||
// PDNS_API_URL and PDNS_API_KEY.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAPIKey, EnvAPIURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
hostURL, err := url.Parse(values[EnvAPIURL])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Host = hostURL
|
||||
config.APIKey = values[EnvAPIKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for pdns.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("pdns: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.APIKey == "" {
|
||||
return nil, errors.New("pdns: API key missing")
|
||||
}
|
||||
|
||||
if config.Host == nil || config.Host.Host == "" {
|
||||
return nil, errors.New("pdns: API URL missing")
|
||||
}
|
||||
|
||||
client := internal.NewClient(config.Host, config.ServerName, config.APIVersion, config.APIKey)
|
||||
|
||||
if config.APIVersion <= 0 {
|
||||
err := client.SetAPIVersion(context.Background())
|
||||
if err != nil {
|
||||
log.Warnf("pdns: failed to get API version %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &DNSProvider{config: config, client: client}, nil
|
||||
}
|
||||
|
||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
zone, err := d.client.GetHostedZone(ctx, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
name := info.EffectiveFQDN
|
||||
if d.client.APIVersion() == 0 {
|
||||
// pre-v1 API wants non-fqdn
|
||||
name = dns01.UnFqdn(info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
// Look for existing records.
|
||||
existingRRSet := findTxtRecord(zone, info.EffectiveFQDN)
|
||||
|
||||
// merge the existing and new records
|
||||
var records []internal.Record
|
||||
if existingRRSet != nil {
|
||||
records = existingRRSet.Records
|
||||
}
|
||||
|
||||
rec := internal.Record{
|
||||
Content: "\"" + info.Value + "\"",
|
||||
Disabled: false,
|
||||
|
||||
// pre-v1 API
|
||||
Type: "TXT",
|
||||
Name: name,
|
||||
TTL: d.config.TTL,
|
||||
}
|
||||
|
||||
rrSets := internal.RRSets{
|
||||
RRSets: []internal.RRSet{
|
||||
{
|
||||
Name: name,
|
||||
ChangeType: "REPLACE",
|
||||
Type: "TXT",
|
||||
Kind: "Master",
|
||||
TTL: d.config.TTL,
|
||||
Records: append(records, rec),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = d.client.UpdateRecords(ctx, zone, rrSets)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
return d.client.Notify(ctx, zone)
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
zone, err := d.client.GetHostedZone(ctx, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
set := findTxtRecord(zone, info.EffectiveFQDN)
|
||||
|
||||
if set == nil {
|
||||
return fmt.Errorf("pdns: no existing record found for %s", info.EffectiveFQDN)
|
||||
}
|
||||
|
||||
rrSets := internal.RRSets{
|
||||
RRSets: []internal.RRSet{
|
||||
{
|
||||
Name: set.Name,
|
||||
Type: set.Type,
|
||||
ChangeType: "DELETE",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = d.client.UpdateRecords(ctx, zone, rrSets)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pdns: %w", err)
|
||||
}
|
||||
|
||||
return d.client.Notify(ctx, zone)
|
||||
}
|
||||
|
||||
func findTxtRecord(zone *internal.HostedZone, fqdn string) *internal.RRSet {
|
||||
for _, set := range zone.RRSets {
|
||||
if set.Type == "TXT" && (set.Name == dns01.UnFqdn(fqdn) || set.Name == fqdn) {
|
||||
return &set
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
37
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/pdns.toml
generated
vendored
Normal file
37
vendor/github.com/go-acme/lego/v4/providers/dns/pdns/pdns.toml
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
Name = "PowerDNS"
|
||||
Description = ''''''
|
||||
URL = "https://www.powerdns.com/"
|
||||
Code = "pdns"
|
||||
Since = "v0.4.0"
|
||||
|
||||
Example = '''
|
||||
PDNS_API_URL=http://pdns-server:80/ \
|
||||
PDNS_API_KEY=xxxx \
|
||||
lego --email you@example.com --dns pdns --domains my.example.org run
|
||||
'''
|
||||
|
||||
Additional = '''
|
||||
## Information
|
||||
|
||||
Tested and confirmed to work with PowerDNS authoritative server 3.4.8 and 4.0.1. Refer to [PowerDNS documentation](https://doc.powerdns.com/md/httpapi/README/) instructions on how to enable the built-in API interface.
|
||||
|
||||
PowerDNS Notes:
|
||||
- PowerDNS API does not currently support SSL, therefore you should take care to ensure that traffic between lego and the PowerDNS API is over a trusted network, VPN etc.
|
||||
- In order to have the SOA serial automatically increment each time the `_acme-challenge` record is added/modified via the API, set `SOA-EDIT-API` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table
|
||||
- Some PowerDNS servers doesn't have root API endpoints enabled and API version autodetection will not work. In that case version number can be defined using `PDNS_API_VERSION`.
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
PDNS_API_KEY = "API key"
|
||||
PDNS_API_URL = "API URL"
|
||||
[Configuration.Additional]
|
||||
PDNS_SERVER_NAME = "Name of the server in the URL, 'localhost' by default"
|
||||
PDNS_API_VERSION = "Skip API version autodetection and use the provided version number."
|
||||
PDNS_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
PDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
PDNS_TTL = "The TTL of the TXT record used for the DNS challenge"
|
||||
PDNS_HTTP_TIMEOUT = "API request timeout"
|
||||
|
||||
[Links]
|
||||
API = "https://doc.powerdns.com/md/httpapi/README/"
|
10
vendor/github.com/go-acme/lego/v4/registration/registar.go
generated
vendored
10
vendor/github.com/go-acme/lego/v4/registration/registar.go
generated
vendored
@ -9,9 +9,11 @@ import (
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
)
|
||||
|
||||
const mailTo = "mailto:"
|
||||
|
||||
// Resource represents all important information about a registration
|
||||
// of which the client needs to keep track itself.
|
||||
// WARNING: will be remove in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855.
|
||||
// WARNING: will be removed in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855.
|
||||
type Resource struct {
|
||||
Body acme.Account `json:"body,omitempty"`
|
||||
URI string `json:"uri,omitempty"`
|
||||
@ -52,7 +54,7 @@ func (r *Registrar) Register(options RegisterOptions) (*Resource, error) {
|
||||
|
||||
if r.user.GetEmail() != "" {
|
||||
log.Infof("acme: Registering account for %s", r.user.GetEmail())
|
||||
accMsg.Contact = []string{"mailto:" + r.user.GetEmail()}
|
||||
accMsg.Contact = []string{mailTo + r.user.GetEmail()}
|
||||
}
|
||||
|
||||
account, err := r.core.Accounts.New(accMsg)
|
||||
@ -76,7 +78,7 @@ func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOption
|
||||
|
||||
if r.user.GetEmail() != "" {
|
||||
log.Infof("acme: Registering account for %s", r.user.GetEmail())
|
||||
accMsg.Contact = []string{"mailto:" + r.user.GetEmail()}
|
||||
accMsg.Contact = []string{mailTo + r.user.GetEmail()}
|
||||
}
|
||||
|
||||
account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded)
|
||||
@ -128,7 +130,7 @@ func (r *Registrar) UpdateRegistration(options RegisterOptions) (*Resource, erro
|
||||
|
||||
if r.user.GetEmail() != "" {
|
||||
log.Infof("acme: Registering account for %s", r.user.GetEmail())
|
||||
accMsg.Contact = []string{"mailto:" + r.user.GetEmail()}
|
||||
accMsg.Contact = []string{mailTo + r.user.GetEmail()}
|
||||
}
|
||||
|
||||
accountURL := r.user.GetRegistration().URI
|
||||
|
2
vendor/github.com/go-jose/go-jose/v4/.gitignore
generated
vendored
Normal file
2
vendor/github.com/go-jose/go-jose/v4/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
jose-util/jose-util
|
||||
jose-util.t.err
|
53
vendor/github.com/go-jose/go-jose/v4/.golangci.yml
generated
vendored
Normal file
53
vendor/github.com/go-jose/go-jose/v4/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# https://github.com/golangci/golangci-lint
|
||||
|
||||
run:
|
||||
skip-files:
|
||||
- doc_test.go
|
||||
modules-download-mode: readonly
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- gochecknoglobals
|
||||
- goconst
|
||||
- lll
|
||||
- maligned
|
||||
- nakedret
|
||||
- scopelint
|
||||
- unparam
|
||||
- funlen # added in 1.18 (requires go-jose changes before it can be enabled)
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 35
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- text: "don't use ALL_CAPS in Go names"
|
||||
linters:
|
||||
- golint
|
||||
- text: "hardcoded credentials"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "weak cryptographic primitive"
|
||||
linters:
|
||||
- gosec
|
||||
- path: json/
|
||||
linters:
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- golint
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- unused
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- scopelint
|
||||
- path: jwk.go
|
||||
linters:
|
||||
- gocyclo
|
33
vendor/github.com/go-jose/go-jose/v4/.travis.yml
generated
vendored
Normal file
33
vendor/github.com/go-jose/go-jose/v4/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
language: go
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
go:
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- tip
|
||||
|
||||
before_script:
|
||||
- export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
before_install:
|
||||
- go get -u github.com/mattn/goveralls github.com/wadey/gocovmerge
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.18.0
|
||||
- pip install cram --user
|
||||
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=profile.cov .
|
||||
- go test -v -covermode=count -coverprofile=cryptosigner/profile.cov ./cryptosigner
|
||||
- go test -v -covermode=count -coverprofile=cipher/profile.cov ./cipher
|
||||
- go test -v -covermode=count -coverprofile=jwt/profile.cov ./jwt
|
||||
- go test -v ./json # no coverage for forked encoding/json package
|
||||
- golangci-lint run
|
||||
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util
|
||||
- cd ..
|
||||
|
||||
after_success:
|
||||
- gocovmerge *.cov */*.cov > merged.coverprofile
|
||||
- goveralls -coverprofile merged.coverprofile -service=travis-ci
|
96
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
Normal file
96
vendor/github.com/go-jose/go-jose/v4/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
# v4.0.4
|
||||
|
||||
## Fixed
|
||||
|
||||
- Reverted "Allow unmarshalling JSONWebKeySets with unsupported key types" as a
|
||||
breaking change. See #136 / #137.
|
||||
|
||||
# v4.0.3
|
||||
|
||||
## Changed
|
||||
|
||||
- Allow unmarshalling JSONWebKeySets with unsupported key types (#130)
|
||||
- Document that OpaqueKeyEncrypter can't be implemented (for now) (#129)
|
||||
- Dependency updates
|
||||
|
||||
# v4.0.2
|
||||
|
||||
## Changed
|
||||
|
||||
- Improved documentation of Verify() to note that JSONWebKeySet is a supported
|
||||
argument type (#104)
|
||||
- Defined exported error values for missing x5c header and unsupported elliptic
|
||||
curves error cases (#117)
|
||||
|
||||
# v4.0.1
|
||||
|
||||
## Fixed
|
||||
|
||||
- An attacker could send a JWE containing compressed data that used large
|
||||
amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
|
||||
Those functions now return an error if the decompressed data would exceed
|
||||
250kB or 10x the compressed size (whichever is larger). Thanks to
|
||||
Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
|
||||
for reporting.
|
||||
|
||||
# v4.0.0
|
||||
|
||||
This release makes some breaking changes in order to more thoroughly
|
||||
address the vulnerabilities discussed in [Three New Attacks Against JSON Web
|
||||
Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
|
||||
token".
|
||||
|
||||
## Changed
|
||||
|
||||
- Limit JWT encryption types (exclude password or public key types) (#78)
|
||||
- Enforce minimum length for HMAC keys (#85)
|
||||
- jwt: match any audience in a list, rather than requiring all audiences (#81)
|
||||
- jwt: accept only Compact Serialization (#75)
|
||||
- jws: Add expected algorithms for signatures (#74)
|
||||
- Require specifying expected algorithms for ParseEncrypted,
|
||||
ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
|
||||
jwt.ParseSignedAndEncrypted (#69, #74)
|
||||
- Usually there is a small, known set of appropriate algorithms for a program
|
||||
to use and it's a mistake to allow unexpected algorithms. For instance the
|
||||
"billion hash attack" relies in part on programs accepting the PBES2
|
||||
encryption algorithm and doing the necessary work even if they weren't
|
||||
specifically configured to allow PBES2.
|
||||
- Revert "Strip padding off base64 strings" (#82)
|
||||
- The specs require base64url encoding without padding.
|
||||
- Minimum supported Go version is now 1.21
|
||||
|
||||
## Added
|
||||
|
||||
- ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
|
||||
- These allow parsing a specific serialization, as opposed to ParseSigned and
|
||||
ParseEncrypted, which try to automatically detect which serialization was
|
||||
provided. It's common to require a specific serialization for a specific
|
||||
protocol - for instance JWT requires Compact serialization.
|
||||
|
||||
[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
||||
|
||||
# v3.0.2
|
||||
|
||||
## Fixed
|
||||
|
||||
- DecryptMulti: handle decompression error (#19)
|
||||
|
||||
## Changed
|
||||
|
||||
- jwe/CompactSerialize: improve performance (#67)
|
||||
- Increase the default number of PBKDF2 iterations to 600k (#48)
|
||||
- Return the proper algorithm for ECDSA keys (#45)
|
||||
|
||||
## Added
|
||||
|
||||
- Add Thumbprint support for opaque signers (#38)
|
||||
|
||||
# v3.0.1
|
||||
|
||||
## Fixed
|
||||
|
||||
- Security issue: an attacker specifying a large "p2c" value can cause
|
||||
JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
|
||||
amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
|
||||
disclosure and to Tom Tervoort for originally publishing the category of attack.
|
||||
https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
|
@ -9,6 +9,7 @@ sure all tests pass by running `go test`, and format your code with `go fmt`.
|
||||
We also recommend using `golint` and `errcheck`.
|
||||
|
||||
Before your code can be accepted into the project you must also sign the
|
||||
[Individual Contributor License Agreement][1].
|
||||
Individual Contributor License Agreement. We use [cla-assistant.io][1] and you
|
||||
will be prompted to sign once a pull request is opened.
|
||||
|
||||
[1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
|
||||
[1]: https://cla-assistant.io/
|
@ -1,10 +1,9 @@
|
||||
# Go JOSE
|
||||
|
||||
[![godoc](http://img.shields.io/badge/godoc-version_1-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1)
|
||||
[![godoc](http://img.shields.io/badge/godoc-version_2-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v2)
|
||||
[![license](http://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/square/go-jose/master/LICENSE)
|
||||
[![build](https://travis-ci.org/square/go-jose.svg?branch=v2)](https://travis-ci.org/square/go-jose)
|
||||
[![coverage](https://coveralls.io/repos/github/square/go-jose/badge.svg?branch=v2)](https://coveralls.io/r/square/go-jose)
|
||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||
[![license](https://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
|
||||
[![test](https://img.shields.io/github/checks-status/go-jose/go-jose/v4)](https://github.com/go-jose/go-jose/actions)
|
||||
|
||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||
and Encryption set of standards. This includes support for JSON Web Encryption,
|
||||
@ -21,13 +20,13 @@ US maintained blocked list.
|
||||
## Overview
|
||||
|
||||
The implementation follows the
|
||||
[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) (RFC 7516),
|
||||
[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
|
||||
[JSON Web Token](http://dx.doi.org/10.17487/RFC7519) (RFC 7519).
|
||||
[JSON Web Encryption](https://dx.doi.org/10.17487/RFC7516) (RFC 7516),
|
||||
[JSON Web Signature](https://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
|
||||
[JSON Web Token](https://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
|
||||
Tables of supported algorithms are shown below. The library supports both
|
||||
the compact and full serialization formats, and has optional support for
|
||||
the compact and JWS/JWE JSON Serialization formats, and has optional support for
|
||||
multiple recipients. It also comes with a small command-line utility
|
||||
([`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util))
|
||||
([`jose-util`](https://pkg.go.dev/github.com/go-jose/go-jose/jose-util))
|
||||
for dealing with JOSE messages in a shell.
|
||||
|
||||
**Note**: We use a forked version of the `encoding/json` package from the Go
|
||||
@ -38,25 +37,22 @@ libraries in other languages.
|
||||
|
||||
### Versions
|
||||
|
||||
We use [gopkg.in](https://gopkg.in) for versioning.
|
||||
[Version 4](https://github.com/go-jose/go-jose)
|
||||
([branch](https://github.com/go-jose/go-jose/tree/main),
|
||||
[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version:
|
||||
|
||||
[Version 2](https://gopkg.in/square/go-jose.v2)
|
||||
([branch](https://github.com/square/go-jose/tree/v2),
|
||||
[doc](https://godoc.org/gopkg.in/square/go-jose.v2)) is the current version:
|
||||
import "github.com/go-jose/go-jose/v4"
|
||||
|
||||
import "gopkg.in/square/go-jose.v2"
|
||||
The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which
|
||||
are still useable but not actively developed anymore.
|
||||
|
||||
The old `v1` branch ([go-jose.v1](https://gopkg.in/square/go-jose.v1)) will
|
||||
still receive backported bug fixes and security fixes, but otherwise
|
||||
development is frozen. All new feature development takes place on the `v2`
|
||||
branch. Version 2 also contains additional sub-packages such as the
|
||||
[jwt](https://godoc.org/gopkg.in/square/go-jose.v2/jwt) implementation
|
||||
contributed by [@shaxbee](https://github.com/shaxbee).
|
||||
Version 3, in this repo, is still receiving security fixes but not functionality
|
||||
updates.
|
||||
|
||||
### Supported algorithms
|
||||
|
||||
See below for a table of supported algorithms. Algorithm identifiers match
|
||||
the names in the [JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518)
|
||||
the names in the [JSON Web Algorithms](https://dx.doi.org/10.17487/RFC7518)
|
||||
standard where possible. The Godoc reference has a list of constants.
|
||||
|
||||
Key encryption | Algorithm identifier(s)
|
||||
@ -99,20 +95,20 @@ allows attaching a key id.
|
||||
|
||||
Algorithm(s) | Corresponding types
|
||||
:------------------------- | -------------------------------
|
||||
RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey)
|
||||
ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey)
|
||||
EdDSA<sup>1</sup> | [ed25519.PublicKey](https://godoc.org/golang.org/x/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://godoc.org/golang.org/x/crypto/ed25519#PrivateKey)
|
||||
RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey)
|
||||
ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey)
|
||||
EdDSA<sup>1</sup> | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey)
|
||||
AES, HMAC | []byte
|
||||
|
||||
<sup>1. Only available in version 2 of the package</sup>
|
||||
<sup>1. Only available in version 2 or later of the package</sup>
|
||||
|
||||
## Examples
|
||||
|
||||
[![godoc](http://img.shields.io/badge/godoc-version_1-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1)
|
||||
[![godoc](http://img.shields.io/badge/godoc-version_2-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v2)
|
||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
|
||||
[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
|
||||
|
||||
Examples can be found in the Godoc
|
||||
reference for this package. The
|
||||
[`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util)
|
||||
[`jose-util`](https://github.com/go-jose/go-jose/tree/v4/jose-util)
|
||||
subdirectory also contains a small command-line utility which might be useful
|
||||
as an example.
|
||||
as an example as well.
|
13
vendor/github.com/go-jose/go-jose/v4/SECURITY.md
generated
vendored
Normal file
13
vendor/github.com/go-jose/go-jose/v4/SECURITY.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
This document explains how to contact the Let's Encrypt security team to report security vulnerabilities.
|
||||
|
||||
## Supported Versions
|
||||
| Version | Supported |
|
||||
| ------- | ----------|
|
||||
| >= v3 | ✓ |
|
||||
| v2 | ✗ |
|
||||
| v1 | ✗ |
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please see [https://letsencrypt.org/contact/#security](https://letsencrypt.org/contact/#security) for the email address to report a vulnerability. Ensure that the subject line for your report contains the word `vulnerability` and is descriptive. Your email should be acknowledged within 24 hours. If you do not receive a response within 24 hours, please follow-up again with another email.
|
@ -20,6 +20,7 @@ import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
@ -28,9 +29,8 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
josecipher "gopkg.in/square/go-jose.v2/cipher"
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// A generic RSA-based encrypter/verifier
|
||||
@ -285,6 +285,9 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm
|
||||
|
||||
switch alg {
|
||||
case RS256, RS384, RS512:
|
||||
// TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the
|
||||
// random parameter is legacy and ignored, and it can be nil.
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1
|
||||
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
|
||||
case PS256, PS384, PS512:
|
||||
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
||||
@ -413,28 +416,28 @@ func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
|
||||
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
||||
epk, err := headers.getEPK()
|
||||
if err != nil {
|
||||
return nil, errors.New("square/go-jose: invalid epk header")
|
||||
return nil, errors.New("go-jose/go-jose: invalid epk header")
|
||||
}
|
||||
if epk == nil {
|
||||
return nil, errors.New("square/go-jose: missing epk header")
|
||||
return nil, errors.New("go-jose/go-jose: missing epk header")
|
||||
}
|
||||
|
||||
publicKey, ok := epk.Key.(*ecdsa.PublicKey)
|
||||
if publicKey == nil || !ok {
|
||||
return nil, errors.New("square/go-jose: invalid epk header")
|
||||
return nil, errors.New("go-jose/go-jose: invalid epk header")
|
||||
}
|
||||
|
||||
if !ctx.privateKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) {
|
||||
return nil, errors.New("square/go-jose: invalid public key in epk header")
|
||||
return nil, errors.New("go-jose/go-jose: invalid public key in epk header")
|
||||
}
|
||||
|
||||
apuData, err := headers.getAPU()
|
||||
if err != nil {
|
||||
return nil, errors.New("square/go-jose: invalid apu header")
|
||||
return nil, errors.New("go-jose/go-jose: invalid apu header")
|
||||
}
|
||||
apvData, err := headers.getAPV()
|
||||
if err != nil {
|
||||
return nil, errors.New("square/go-jose: invalid apv header")
|
||||
return nil, errors.New("go-jose/go-jose: invalid apv header")
|
||||
}
|
||||
|
||||
deriveKey := func(algID string, size int) []byte {
|
||||
@ -489,7 +492,7 @@ func (ctx edEncrypterVerifier) verifyPayload(payload []byte, signature []byte, a
|
||||
}
|
||||
ok := ed25519.Verify(ctx.publicKey, payload, signature)
|
||||
if !ok {
|
||||
return errors.New("square/go-jose: ed25519 signature failed to verify")
|
||||
return errors.New("go-jose/go-jose: ed25519 signature failed to verify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -513,7 +516,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm)
|
||||
|
||||
curveBits := ctx.privateKey.Curve.Params().BitSize
|
||||
if expectedBitSize != curveBits {
|
||||
return Signature{}, fmt.Errorf("square/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits)
|
||||
return Signature{}, fmt.Errorf("go-jose/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits)
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
@ -571,7 +574,7 @@ func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, a
|
||||
}
|
||||
|
||||
if len(signature) != 2*keySize {
|
||||
return fmt.Errorf("square/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize)
|
||||
return fmt.Errorf("go-jose/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize)
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
@ -585,7 +588,7 @@ func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, a
|
||||
|
||||
match := ecdsa.Verify(ctx.publicKey, hashed, r, s)
|
||||
if !match {
|
||||
return errors.New("square/go-jose: ecdsa signature failed to verify")
|
||||
return errors.New("go-jose/go-jose: ecdsa signature failed to verify")
|
||||
}
|
||||
|
||||
return nil
|
@ -101,23 +101,23 @@ func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
// Open decrypts and authenticates the ciphertext.
|
||||
func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
if len(ciphertext) < ctx.authtagBytes {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (too short)")
|
||||
return nil, errors.New("go-jose/go-jose: invalid ciphertext (too short)")
|
||||
}
|
||||
|
||||
offset := len(ciphertext) - ctx.authtagBytes
|
||||
expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset])
|
||||
match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:])
|
||||
if match != 1 {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (auth tag mismatch)")
|
||||
return nil, errors.New("go-jose/go-jose: invalid ciphertext (auth tag mismatch)")
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce)
|
||||
|
||||
// Make copy of ciphertext buffer, don't want to modify in place
|
||||
buffer := append([]byte{}, []byte(ciphertext[:offset])...)
|
||||
buffer := append([]byte{}, ciphertext[:offset]...)
|
||||
|
||||
if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)")
|
||||
return nil, errors.New("go-jose/go-jose: invalid ciphertext (invalid length)")
|
||||
}
|
||||
|
||||
cbc.CryptBlocks(buffer, buffer)
|
||||
@ -177,19 +177,19 @@ func padBuffer(buffer []byte, blockSize int) []byte {
|
||||
// Remove padding
|
||||
func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) {
|
||||
if len(buffer)%blockSize != 0 {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
return nil, errors.New("go-jose/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
last := buffer[len(buffer)-1]
|
||||
count := int(last)
|
||||
|
||||
if count == 0 || count > blockSize || count > len(buffer) {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
return nil, errors.New("go-jose/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
padding := bytes.Repeat([]byte{last}, count)
|
||||
if !bytes.HasSuffix(buffer, padding) {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
return nil, errors.New("go-jose/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
return buffer[:len(buffer)-count], nil
|
@ -28,7 +28,7 @@ var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}
|
||||
// KeyWrap implements NIST key wrapping; it wraps a content encryption key (cek) with the given block cipher.
|
||||
func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) {
|
||||
if len(cek)%8 != 0 {
|
||||
return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks")
|
||||
return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks")
|
||||
}
|
||||
|
||||
n := len(cek) / 8
|
||||
@ -51,7 +51,7 @@ func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) {
|
||||
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
buffer[i] = buffer[i] ^ tBytes[i]
|
||||
buffer[i] ^= tBytes[i]
|
||||
}
|
||||
copy(r[t%n], buffer[8:])
|
||||
}
|
||||
@ -68,7 +68,7 @@ func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) {
|
||||
// KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher.
|
||||
func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) {
|
||||
if len(ciphertext)%8 != 0 {
|
||||
return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks")
|
||||
return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks")
|
||||
}
|
||||
|
||||
n := (len(ciphertext) / 8) - 1
|
||||
@ -87,7 +87,7 @@ func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) {
|
||||
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
buffer[i] = buffer[i] ^ tBytes[i]
|
||||
buffer[i] ^= tBytes[i]
|
||||
}
|
||||
copy(buffer[8:], r[t%n])
|
||||
|
||||
@ -97,7 +97,7 @@ func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(buffer[:8], defaultIV) == 0 {
|
||||
return nil, errors.New("square/go-jose: failed to unwrap key")
|
||||
return nil, errors.New("go-jose/go-jose: failed to unwrap key")
|
||||
}
|
||||
|
||||
out := make([]byte, n*8)
|
@ -21,9 +21,8 @@ import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// Encrypter represents an encrypter which produces an encrypted JWE object.
|
||||
@ -76,14 +75,24 @@ type recipientKeyInfo struct {
|
||||
type EncrypterOptions struct {
|
||||
Compression CompressionAlgorithm
|
||||
|
||||
// Optional map of additional keys to be inserted into the protected header
|
||||
// of a JWS object. Some specifications which make use of JWS like to insert
|
||||
// additional values here. All values must be JSON-serializable.
|
||||
// Optional map of name/value pairs to be inserted into the protected
|
||||
// header of a JWS object. Some specifications which make use of
|
||||
// JWS require additional values here.
|
||||
//
|
||||
// Values will be serialized by [json.Marshal] and must be valid inputs to
|
||||
// that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
||||
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
|
||||
// if necessary. It returns itself and so can be used in a fluent style.
|
||||
// if necessary, and returns the updated EncrypterOptions.
|
||||
//
|
||||
// The v parameter will be serialized by [json.Marshal] and must be a valid
|
||||
// input to that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
|
||||
if eo.ExtraHeaders == nil {
|
||||
eo.ExtraHeaders = map[HeaderKey]interface{}{}
|
||||
@ -111,7 +120,17 @@ func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
|
||||
// default of 100000 will be used for the count and a 128-bit random salt will
|
||||
// be generated.
|
||||
type Recipient struct {
|
||||
Algorithm KeyAlgorithm
|
||||
Algorithm KeyAlgorithm
|
||||
// Key must have one of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *rsa.PublicKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - []byte (a symmetric key)
|
||||
// - Any type that satisfies the OpaqueKeyEncrypter interface
|
||||
//
|
||||
// The type of Key must match the value of Algorithm.
|
||||
Key interface{}
|
||||
KeyID string
|
||||
PBES2Count int
|
||||
@ -150,16 +169,17 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
|
||||
switch rcpt.Algorithm {
|
||||
case DIRECT:
|
||||
// Direct encryption mode must be treated differently
|
||||
if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) {
|
||||
keyBytes, ok := rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if encrypter.cipher.keySize() != len(rawKey.([]byte)) {
|
||||
if encrypter.cipher.keySize() != len(keyBytes) {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
encrypter.keyGenerator = staticKeyGenerator{
|
||||
key: rawKey.([]byte),
|
||||
key: keyBytes,
|
||||
}
|
||||
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, rawKey.([]byte))
|
||||
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, keyBytes)
|
||||
recipientInfo.keyID = keyID
|
||||
if rcpt.KeyID != "" {
|
||||
recipientInfo.keyID = rcpt.KeyID
|
||||
@ -168,16 +188,16 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
|
||||
return encrypter, nil
|
||||
case ECDH_ES:
|
||||
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
|
||||
typeOf := reflect.TypeOf(rawKey)
|
||||
if typeOf != reflect.TypeOf(&ecdsa.PublicKey{}) {
|
||||
keyDSA, ok := rawKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
encrypter.keyGenerator = ecKeyGenerator{
|
||||
size: encrypter.cipher.keySize(),
|
||||
algID: string(enc),
|
||||
publicKey: rawKey.(*ecdsa.PublicKey),
|
||||
publicKey: keyDSA,
|
||||
}
|
||||
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, rawKey.(*ecdsa.PublicKey))
|
||||
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, keyDSA)
|
||||
recipientInfo.keyID = keyID
|
||||
if rcpt.KeyID != "" {
|
||||
recipientInfo.keyID = rcpt.KeyID
|
||||
@ -201,8 +221,8 @@ func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *Encrypter
|
||||
if cipher == nil {
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
if rcpts == nil || len(rcpts) == 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: recipients is nil or empty")
|
||||
if len(rcpts) == 0 {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: recipients is nil or empty")
|
||||
}
|
||||
|
||||
encrypter := &genericEncrypter{
|
||||
@ -234,7 +254,7 @@ func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) {
|
||||
|
||||
switch recipient.Algorithm {
|
||||
case DIRECT, ECDH_ES:
|
||||
return fmt.Errorf("square/go-jose: key algorithm '%s' not supported in multi-recipient mode", recipient.Algorithm)
|
||||
return fmt.Errorf("go-jose/go-jose: key algorithm '%s' not supported in multi-recipient mode", recipient.Algorithm)
|
||||
}
|
||||
|
||||
recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key)
|
||||
@ -270,9 +290,8 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
|
||||
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
||||
recipient.keyID = encryptionKey.KeyID
|
||||
return recipient, err
|
||||
}
|
||||
if encrypter, ok := encryptionKey.(OpaqueKeyEncrypter); ok {
|
||||
return newOpaqueKeyEncrypter(alg, encrypter)
|
||||
case OpaqueKeyEncrypter:
|
||||
return newOpaqueKeyEncrypter(alg, encryptionKey)
|
||||
}
|
||||
return recipientKeyInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
@ -300,11 +319,11 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
|
||||
return newDecrypter(decryptionKey.Key)
|
||||
case *JSONWebKey:
|
||||
return newDecrypter(decryptionKey.Key)
|
||||
case OpaqueKeyDecrypter:
|
||||
return &opaqueKeyDecrypter{decrypter: decryptionKey}, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if okd, ok := decryptionKey.(OpaqueKeyDecrypter); ok {
|
||||
return &opaqueKeyDecrypter{decrypter: okd}, nil
|
||||
}
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
// Implementation of encrypt method producing a JWE object.
|
||||
@ -326,7 +345,7 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe
|
||||
obj.recipients = make([]recipientInfo, len(ctx.recipients))
|
||||
|
||||
if len(ctx.recipients) == 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: no recipients to encrypt to")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: no recipients to encrypt to")
|
||||
}
|
||||
|
||||
cek, headers, err := ctx.keyGenerator.genKey()
|
||||
@ -403,33 +422,55 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt and validate the object and return the plaintext. Note that this
|
||||
// function does not support multi-recipient, if you desire multi-recipient
|
||||
// Decrypt and validate the object and return the plaintext. This
|
||||
// function does not support multi-recipient. If you desire multi-recipient
|
||||
// decryption use DecryptMulti instead.
|
||||
//
|
||||
// The decryptionKey argument must contain a private or symmetric key
|
||||
// and must have one of these types:
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PrivateKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - *JSONWebKeySet
|
||||
// - JSONWebKeySet
|
||||
// - []byte (a symmetric key)
|
||||
// - string (a symmetric key)
|
||||
// - Any type that satisfies the OpaqueKeyDecrypter interface.
|
||||
//
|
||||
// Note that ed25519 is only available for signatures, not encryption, so is
|
||||
// not an option here.
|
||||
//
|
||||
// Automatically decompresses plaintext, but returns an error if the decompressed
|
||||
// data would be >250kB or >10x the size of the compressed data, whichever is larger.
|
||||
func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
|
||||
headers := obj.mergedHeaders(nil)
|
||||
|
||||
if len(obj.recipients) > 1 {
|
||||
return nil, errors.New("square/go-jose: too many recipients in payload; expecting only one")
|
||||
return nil, errors.New("go-jose/go-jose: too many recipients in payload; expecting only one")
|
||||
}
|
||||
|
||||
critical, err := headers.getCritical()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid crit header")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
|
||||
}
|
||||
|
||||
if len(critical) > 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: unsupported crit header")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
||||
}
|
||||
|
||||
decrypter, err := newDecrypter(decryptionKey)
|
||||
key, err := tryJWKS(decryptionKey, obj.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, err := newDecrypter(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipher := getContentCipher(headers.getEncryption())
|
||||
if cipher == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.getEncryption()))
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(headers.getEncryption()))
|
||||
}
|
||||
|
||||
generator := randomKeyGenerator{
|
||||
@ -461,28 +502,41 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
|
||||
// The "zip" header parameter may only be present in the protected header.
|
||||
if comp := obj.protected.getCompression(); comp != "" {
|
||||
plaintext, err = decompress(comp, plaintext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return plaintext, err
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// DecryptMulti decrypts and validates the object and returns the plaintexts,
|
||||
// with support for multiple recipients. It returns the index of the recipient
|
||||
// for which the decryption was successful, the merged headers for that recipient,
|
||||
// and the plaintext.
|
||||
//
|
||||
// The decryptionKey argument must have one of the types allowed for the
|
||||
// decryptionKey argument of Decrypt().
|
||||
//
|
||||
// Automatically decompresses plaintext, but returns an error if the decompressed
|
||||
// data would be >250kB or >3x the size of the compressed data, whichever is larger.
|
||||
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
|
||||
globalHeaders := obj.mergedHeaders(nil)
|
||||
|
||||
critical, err := globalHeaders.getCritical()
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, fmt.Errorf("square/go-jose: invalid crit header")
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
|
||||
}
|
||||
|
||||
if len(critical) > 0 {
|
||||
return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported crit header")
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
|
||||
}
|
||||
|
||||
decrypter, err := newDecrypter(decryptionKey)
|
||||
key, err := tryJWKS(decryptionKey, obj.Header)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, err
|
||||
}
|
||||
decrypter, err := newDecrypter(key)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, err
|
||||
}
|
||||
@ -490,7 +544,7 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
|
||||
encryption := globalHeaders.getEncryption()
|
||||
cipher := getContentCipher(encryption)
|
||||
if cipher == nil {
|
||||
return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(encryption))
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(encryption))
|
||||
}
|
||||
|
||||
generator := randomKeyGenerator{
|
||||
@ -524,18 +578,21 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
|
||||
}
|
||||
}
|
||||
|
||||
if plaintext == nil || err != nil {
|
||||
if plaintext == nil {
|
||||
return -1, Header{}, nil, ErrCryptoFailure
|
||||
}
|
||||
|
||||
// The "zip" header parameter may only be present in the protected header.
|
||||
if comp := obj.protected.getCompression(); comp != "" {
|
||||
plaintext, err = decompress(comp, plaintext)
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
sanitized, err := headers.sanitized()
|
||||
if err != nil {
|
||||
return -1, Header{}, nil, fmt.Errorf("square/go-jose: failed to sanitize header: %v", err)
|
||||
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to sanitize header: %v", err)
|
||||
}
|
||||
|
||||
return index, sanitized, plaintext, err
|
@ -15,13 +15,11 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||
and Encryption set of standards. It implements encryption and signing based on
|
||||
the JSON Web Encryption and JSON Web Signature standards, with optional JSON
|
||||
Web Token support available in a sub-package. The library supports both the
|
||||
compact and full serialization formats, and has optional support for multiple
|
||||
the JSON Web Encryption and JSON Web Signature standards, with optional JSON Web
|
||||
Token support available in a sub-package. The library supports both the compact
|
||||
and JWS/JWE JSON Serialization formats, and has optional support for multiple
|
||||
recipients.
|
||||
|
||||
*/
|
||||
package jose
|
@ -21,12 +21,13 @@ import (
|
||||
"compress/flate"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// Helper function to serialize known-good objects.
|
||||
@ -41,7 +42,7 @@ func mustSerializeJSON(value interface{}) []byte {
|
||||
// MarshalJSON will happily serialize it as the top-level value "null". If
|
||||
// that value is then embedded in another operation, for instance by being
|
||||
// base64-encoded and fed as input to a signing algorithm
|
||||
// (https://github.com/square/go-jose/issues/22), the result will be
|
||||
// (https://github.com/go-jose/go-jose/issues/22), the result will be
|
||||
// incorrect. Because this method is intended for known-good objects, and a nil
|
||||
// pointer is not a known-good object, we are free to panic in this case.
|
||||
// Note: It's not possible to directly check whether the data pointed at by an
|
||||
@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compress with DEFLATE
|
||||
// deflate compresses the input.
|
||||
func deflate(input []byte) ([]byte, error) {
|
||||
output := new(bytes.Buffer)
|
||||
|
||||
@ -97,15 +98,24 @@ func deflate(input []byte) ([]byte, error) {
|
||||
return output.Bytes(), err
|
||||
}
|
||||
|
||||
// Decompress with DEFLATE
|
||||
// inflate decompresses the input.
|
||||
//
|
||||
// Errors if the decompressed data would be >250kB or >10x the size of the
|
||||
// compressed data, whichever is larger.
|
||||
func inflate(input []byte) ([]byte, error) {
|
||||
output := new(bytes.Buffer)
|
||||
reader := flate.NewReader(bytes.NewBuffer(input))
|
||||
|
||||
_, err := io.Copy(output, reader)
|
||||
if err != nil {
|
||||
maxCompressedSize := max(250_000, 10*int64(len(input)))
|
||||
|
||||
limit := maxCompressedSize + 1
|
||||
n, err := io.CopyN(output, reader, limit)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
if n == limit {
|
||||
return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
|
||||
}
|
||||
|
||||
err = reader.Close()
|
||||
return output.Bytes(), err
|
||||
@ -127,7 +137,7 @@ func newBuffer(data []byte) *byteBuffer {
|
||||
|
||||
func newFixedSizeBuffer(data []byte, length int) *byteBuffer {
|
||||
if len(data) > length {
|
||||
panic("square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)")
|
||||
panic("go-jose/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)")
|
||||
}
|
||||
pad := make([]byte, length-len(data))
|
||||
return newBuffer(append(pad, data...))
|
||||
@ -183,3 +193,36 @@ func (b byteBuffer) bigInt() *big.Int {
|
||||
func (b byteBuffer) toInt() int {
|
||||
return int(b.bigInt().Int64())
|
||||
}
|
||||
|
||||
func base64EncodeLen(sl []byte) int {
|
||||
return base64.RawURLEncoding.EncodedLen(len(sl))
|
||||
}
|
||||
|
||||
func base64JoinWithDots(inputs ...[]byte) string {
|
||||
if len(inputs) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Count of dots.
|
||||
totalCount := len(inputs) - 1
|
||||
|
||||
for _, input := range inputs {
|
||||
totalCount += base64EncodeLen(input)
|
||||
}
|
||||
|
||||
out := make([]byte, totalCount)
|
||||
startEncode := 0
|
||||
for i, input := range inputs {
|
||||
base64.RawURLEncoding.Encode(out[startEncode:], input)
|
||||
|
||||
if i == len(inputs)-1 {
|
||||
continue
|
||||
}
|
||||
|
||||
startEncode += base64EncodeLen(input)
|
||||
out[startEncode] = '.'
|
||||
startEncode++
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
@ -75,14 +75,13 @@ import (
|
||||
//
|
||||
// The JSON null value unmarshals into an interface, map, pointer, or slice
|
||||
// by setting that Go value to nil. Because null is often used in JSON to mean
|
||||
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
|
||||
// “not present,” unmarshaling a JSON null into any other Go type has no effect
|
||||
// on the value and produces no error.
|
||||
//
|
||||
// When unmarshaling quoted strings, invalid UTF-8 or
|
||||
// invalid UTF-16 surrogate pairs are not treated as an error.
|
||||
// Instead, they are replaced by the Unicode replacement
|
||||
// character U+FFFD.
|
||||
//
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
// Check for well-formedness.
|
||||
// Avoids filling out half a data structure
|
@ -58,6 +58,7 @@ import (
|
||||
// becomes a member of the object unless
|
||||
// - the field's tag is "-", or
|
||||
// - the field is empty and its tag specifies the "omitempty" option.
|
||||
//
|
||||
// The empty values are false, 0, any
|
||||
// nil pointer or interface value, and any array, slice, map, or string of
|
||||
// length zero. The object's default key string is the struct field name
|
||||
@ -65,28 +66,28 @@ import (
|
||||
// the struct field's tag value is the key name, followed by an optional comma
|
||||
// and options. Examples:
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `json:"-"`
|
||||
// // Field is ignored by this package.
|
||||
// Field int `json:"-"`
|
||||
//
|
||||
// // Field appears in JSON as key "myName".
|
||||
// Field int `json:"myName"`
|
||||
// // Field appears in JSON as key "myName".
|
||||
// Field int `json:"myName"`
|
||||
//
|
||||
// // Field appears in JSON as key "myName" and
|
||||
// // the field is omitted from the object if its value is empty,
|
||||
// // as defined above.
|
||||
// Field int `json:"myName,omitempty"`
|
||||
// // Field appears in JSON as key "myName" and
|
||||
// // the field is omitted from the object if its value is empty,
|
||||
// // as defined above.
|
||||
// Field int `json:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in JSON as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// // Note the leading comma.
|
||||
// Field int `json:",omitempty"`
|
||||
// // Field appears in JSON as key "Field" (the default), but
|
||||
// // the field is skipped if empty.
|
||||
// // Note the leading comma.
|
||||
// Field int `json:",omitempty"`
|
||||
//
|
||||
// The "string" option signals that a field is stored as JSON inside a
|
||||
// JSON-encoded string. It applies only to fields of string, floating point,
|
||||
// integer, or boolean types. This extra level of encoding is sometimes used
|
||||
// when communicating with JavaScript programs:
|
||||
//
|
||||
// Int64String int64 `json:",string"`
|
||||
// Int64String int64 `json:",string"`
|
||||
//
|
||||
// The key name will be used if it's a non-empty string consisting of
|
||||
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
|
||||
@ -133,7 +134,6 @@ import (
|
||||
// JSON cannot represent cyclic data structures and Marshal does not
|
||||
// handle them. Passing cyclic structures to Marshal will result in
|
||||
// an infinite recursion.
|
||||
//
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
e := &encodeState{}
|
||||
err := e.marshal(v)
|
||||
@ -648,7 +648,7 @@ func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
|
||||
// for large buffers, avoid unnecessary extra temporary
|
||||
// buffer space.
|
||||
enc := base64.NewEncoder(base64.StdEncoding, e)
|
||||
enc.Write(s)
|
||||
_, _ = enc.Write(s)
|
||||
enc.Close()
|
||||
}
|
||||
e.WriteByte('"')
|
@ -240,7 +240,6 @@ var _ Unmarshaler = (*RawMessage)(nil)
|
||||
// Number, for JSON numbers
|
||||
// string, for JSON string literals
|
||||
// nil, for JSON null
|
||||
//
|
||||
type Token interface{}
|
||||
|
||||
const (
|
149
vendor/gopkg.in/square/go-jose.v2/jwe.go → vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
149
vendor/gopkg.in/square/go-jose.v2/jwe.go → vendor/github.com/go-jose/go-jose/v4/jwe.go
generated
vendored
@ -18,10 +18,11 @@ package jose
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
|
||||
@ -86,11 +87,12 @@ func (obj JSONWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader {
|
||||
func (obj JSONWebEncryption) computeAuthData() []byte {
|
||||
var protected string
|
||||
|
||||
if obj.original != nil && obj.original.Protected != nil {
|
||||
switch {
|
||||
case obj.original != nil && obj.original.Protected != nil:
|
||||
protected = obj.original.Protected.base64()
|
||||
} else if obj.protected != nil {
|
||||
case obj.protected != nil:
|
||||
protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected)))
|
||||
} else {
|
||||
default:
|
||||
protected = ""
|
||||
}
|
||||
|
||||
@ -103,29 +105,75 @@ func (obj JSONWebEncryption) computeAuthData() []byte {
|
||||
return output
|
||||
}
|
||||
|
||||
// ParseEncrypted parses an encrypted message in compact or full serialization format.
|
||||
func ParseEncrypted(input string) (*JSONWebEncryption, error) {
|
||||
input = stripWhitespace(input)
|
||||
if strings.HasPrefix(input, "{") {
|
||||
return parseEncryptedFull(input)
|
||||
func containsKeyAlgorithm(haystack []KeyAlgorithm, needle KeyAlgorithm) bool {
|
||||
for _, algorithm := range haystack {
|
||||
if algorithm == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return parseEncryptedCompact(input)
|
||||
return false
|
||||
}
|
||||
|
||||
// parseEncryptedFull parses a message in compact format.
|
||||
func parseEncryptedFull(input string) (*JSONWebEncryption, error) {
|
||||
func containsContentEncryption(haystack []ContentEncryption, needle ContentEncryption) bool {
|
||||
for _, algorithm := range haystack {
|
||||
if algorithm == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ParseEncrypted parses an encrypted message in JWE Compact or JWE JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.1
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.2
|
||||
//
|
||||
// The keyAlgorithms and contentEncryption parameters are used to validate the "alg" and "enc"
|
||||
// header parameters respectively. They must be nonempty, and each "alg" or "enc" header in
|
||||
// parsed data must contain a value that is present in the corresponding parameter. That
|
||||
// includes the protected and unprotected headers as well as all recipients. To accept
|
||||
// multiple algorithms, pass a slice of all the algorithms you want to accept.
|
||||
func ParseEncrypted(input string,
|
||||
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||
contentEncryption []ContentEncryption,
|
||||
) (*JSONWebEncryption, error) {
|
||||
input = stripWhitespace(input)
|
||||
if strings.HasPrefix(input, "{") {
|
||||
return ParseEncryptedJSON(input, keyEncryptionAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
return ParseEncryptedCompact(input, keyEncryptionAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
// ParseEncryptedJSON parses a message in JWE JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.2
|
||||
func ParseEncryptedJSON(
|
||||
input string,
|
||||
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||
contentEncryption []ContentEncryption,
|
||||
) (*JSONWebEncryption, error) {
|
||||
var parsed rawJSONWebEncryption
|
||||
err := json.Unmarshal([]byte(input), &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parsed.sanitized()
|
||||
return parsed.sanitized(keyEncryptionAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
// sanitized produces a cleaned-up JWE object from the raw JSON.
|
||||
func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
func (parsed *rawJSONWebEncryption) sanitized(
|
||||
keyEncryptionAlgorithms []KeyAlgorithm,
|
||||
contentEncryption []ContentEncryption,
|
||||
) (*JSONWebEncryption, error) {
|
||||
if len(keyEncryptionAlgorithms) == 0 {
|
||||
return nil, errors.New("go-jose/go-jose: no key algorithms provided")
|
||||
}
|
||||
if len(contentEncryption) == 0 {
|
||||
return nil, errors.New("go-jose/go-jose: no content encryption algorithms provided")
|
||||
}
|
||||
|
||||
obj := &JSONWebEncryption{
|
||||
original: parsed,
|
||||
unprotected: parsed.Unprotected,
|
||||
@ -146,7 +194,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
|
||||
err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64())
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64())
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +204,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
mergedHeaders := obj.mergedHeaders(nil)
|
||||
obj.Header, err = mergedHeaders.sanitized()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders)
|
||||
}
|
||||
|
||||
if len(parsed.Recipients) == 0 {
|
||||
@ -184,10 +232,31 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, recipient := range obj.recipients {
|
||||
for i, recipient := range obj.recipients {
|
||||
headers := obj.mergedHeaders(&recipient)
|
||||
if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
|
||||
return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers")
|
||||
if headers.getAlgorithm() == "" {
|
||||
return nil, fmt.Errorf(`go-jose/go-jose: recipient %d: missing header "alg"`, i)
|
||||
}
|
||||
if headers.getEncryption() == "" {
|
||||
return nil, fmt.Errorf(`go-jose/go-jose: recipient %d: missing header "enc"`, i)
|
||||
}
|
||||
err := validateAlgEnc(headers, keyEncryptionAlgorithms, contentEncryption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: recipient %d: %s", i, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if obj.protected != nil {
|
||||
err := validateAlgEnc(*obj.protected, keyEncryptionAlgorithms, contentEncryption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: protected header: %s", err)
|
||||
}
|
||||
}
|
||||
if obj.unprotected != nil {
|
||||
err := validateAlgEnc(*obj.unprotected, keyEncryptionAlgorithms, contentEncryption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unprotected header: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,11 +268,29 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// parseEncryptedCompact parses a message in compact format.
|
||||
func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
||||
func validateAlgEnc(headers rawHeader, keyAlgorithms []KeyAlgorithm, contentEncryption []ContentEncryption) error {
|
||||
alg := headers.getAlgorithm()
|
||||
enc := headers.getEncryption()
|
||||
if alg != "" && !containsKeyAlgorithm(keyAlgorithms, alg) {
|
||||
return fmt.Errorf("unexpected key algorithm %q; expected %q", alg, keyAlgorithms)
|
||||
}
|
||||
if alg != "" && !containsContentEncryption(contentEncryption, enc) {
|
||||
return fmt.Errorf("unexpected content encryption algorithm %q; expected %q", enc, contentEncryption)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseEncryptedCompact parses a message in JWE Compact Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.1
|
||||
func ParseEncryptedCompact(
|
||||
input string,
|
||||
keyAlgorithms []KeyAlgorithm,
|
||||
contentEncryption []ContentEncryption,
|
||||
) (*JSONWebEncryption, error) {
|
||||
parts := strings.Split(input, ".")
|
||||
if len(parts) != 5 {
|
||||
return nil, fmt.Errorf("square/go-jose: compact JWE format must have five parts")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWE format must have five parts")
|
||||
}
|
||||
|
||||
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
@ -239,7 +326,7 @@ func parseEncryptedCompact(input string) (*JSONWebEncryption, error) {
|
||||
Tag: newBuffer(tag),
|
||||
}
|
||||
|
||||
return raw.sanitized()
|
||||
return raw.sanitized(keyAlgorithms, contentEncryption)
|
||||
}
|
||||
|
||||
// CompactSerialize serializes an object using the compact serialization format.
|
||||
@ -251,13 +338,13 @@ func (obj JSONWebEncryption) CompactSerialize() (string, error) {
|
||||
|
||||
serializedProtected := mustSerializeJSON(obj.protected)
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s.%s.%s.%s.%s",
|
||||
base64.RawURLEncoding.EncodeToString(serializedProtected),
|
||||
base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey),
|
||||
base64.RawURLEncoding.EncodeToString(obj.iv),
|
||||
base64.RawURLEncoding.EncodeToString(obj.ciphertext),
|
||||
base64.RawURLEncoding.EncodeToString(obj.tag)), nil
|
||||
return base64JoinWithDots(
|
||||
serializedProtected,
|
||||
obj.recipients[0].encryptedKey,
|
||||
obj.iv,
|
||||
obj.ciphertext,
|
||||
obj.tag,
|
||||
), nil
|
||||
}
|
||||
|
||||
// FullSerialize serializes an object using the full JSON serialization format.
|
163
vendor/gopkg.in/square/go-jose.v2/jwk.go → vendor/github.com/go-jose/go-jose/v4/jwk.go
generated
vendored
163
vendor/gopkg.in/square/go-jose.v2/jwk.go → vendor/github.com/go-jose/go-jose/v4/jwk.go
generated
vendored
@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
@ -34,9 +35,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing.
|
||||
@ -63,14 +62,26 @@ type rawJSONWebKey struct {
|
||||
Qi *byteBuffer `json:"qi,omitempty"`
|
||||
// Certificates
|
||||
X5c []string `json:"x5c,omitempty"`
|
||||
X5u *url.URL `json:"x5u,omitempty"`
|
||||
X5u string `json:"x5u,omitempty"`
|
||||
X5tSHA1 string `json:"x5t,omitempty"`
|
||||
X5tSHA256 string `json:"x5t#S256,omitempty"`
|
||||
}
|
||||
|
||||
// JSONWebKey represents a public or private key in JWK format.
|
||||
// JSONWebKey represents a public or private key in JWK format. It can be
|
||||
// marshaled into JSON and unmarshaled from JSON.
|
||||
type JSONWebKey struct {
|
||||
// Cryptographic key, can be a symmetric or asymmetric key.
|
||||
// Key is the Go in-memory representation of this key. It must have one
|
||||
// of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - ed25519.PrivateKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PublicKey
|
||||
// - *rsa.PrivateKey
|
||||
// - []byte (a symmetric key)
|
||||
//
|
||||
// When marshaling this JSONWebKey into JSON, the "kty" header parameter
|
||||
// will be automatically set based on the type of this field.
|
||||
Key interface{}
|
||||
// Key identifier, parsed from `kid` header.
|
||||
KeyID string
|
||||
@ -110,7 +121,7 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
|
||||
case []byte:
|
||||
raw, err = fromSymmetricKey(key)
|
||||
default:
|
||||
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -129,13 +140,13 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
|
||||
x5tSHA256Len := len(k.CertificateThumbprintSHA256)
|
||||
if x5tSHA1Len > 0 {
|
||||
if x5tSHA1Len != sha1.Size {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len)
|
||||
}
|
||||
raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1)
|
||||
}
|
||||
if x5tSHA256Len > 0 {
|
||||
if x5tSHA256Len != sha256.Size {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len)
|
||||
}
|
||||
raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256)
|
||||
}
|
||||
@ -149,14 +160,16 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
|
||||
expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw)
|
||||
|
||||
if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) {
|
||||
return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain")
|
||||
return nil, errors.New("go-jose/go-jose: invalid SHA-1 thumbprint, does not match cert chain")
|
||||
}
|
||||
if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) {
|
||||
return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain")
|
||||
return nil, errors.New("go-jose/go-jose: invalid or SHA-256 thumbprint, does not match cert chain")
|
||||
}
|
||||
}
|
||||
|
||||
raw.X5u = k.CertificatesURL
|
||||
if k.CertificatesURL != nil {
|
||||
raw.X5u = k.CertificatesURL.String()
|
||||
}
|
||||
|
||||
return json.Marshal(raw)
|
||||
}
|
||||
@ -171,7 +184,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
certs, err := parseCertificateChain(raw.X5c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err)
|
||||
return fmt.Errorf("go-jose/go-jose: failed to unmarshal x5c field: %s", err)
|
||||
}
|
||||
|
||||
var key interface{}
|
||||
@ -211,7 +224,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
}
|
||||
case "oct":
|
||||
if certPub != nil {
|
||||
return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain")
|
||||
}
|
||||
key, err = raw.symmetricKey()
|
||||
case "OKP":
|
||||
@ -226,10 +239,10 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
keyPub = key
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv)
|
||||
err = fmt.Errorf("go-jose/go-jose: unknown curve %s'", raw.Crv)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty)
|
||||
err = fmt.Errorf("go-jose/go-jose: unknown json web key type '%s'", raw.Kty)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -238,19 +251,24 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
if certPub != nil && keyPub != nil {
|
||||
if !reflect.DeepEqual(certPub, keyPub) {
|
||||
return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, public keys in key and x5c fields do not match")
|
||||
}
|
||||
}
|
||||
|
||||
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs}
|
||||
|
||||
k.CertificatesURL = raw.X5u
|
||||
if raw.X5u != "" {
|
||||
k.CertificatesURL, err = url.Parse(raw.X5u)
|
||||
if err != nil {
|
||||
return fmt.Errorf("go-jose/go-jose: invalid JWK, x5u header is invalid URL: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// x5t parameters are base64url-encoded SHA thumbprints
|
||||
// See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8
|
||||
x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1)
|
||||
if err != nil {
|
||||
return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t header has invalid encoding")
|
||||
}
|
||||
|
||||
// RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex,
|
||||
@ -260,7 +278,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
if len(x5tSHA1bytes) == 2*sha1.Size {
|
||||
hx, err := hex.DecodeString(string(x5tSHA1bytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err)
|
||||
return fmt.Errorf("go-jose/go-jose: invalid JWK, unable to hex decode x5t: %v", err)
|
||||
|
||||
}
|
||||
x5tSHA1bytes = hx
|
||||
@ -270,13 +288,13 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
|
||||
x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256)
|
||||
if err != nil {
|
||||
return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t#S256 header has invalid encoding")
|
||||
}
|
||||
|
||||
if len(x5tSHA256bytes) == 2*sha256.Size {
|
||||
hx256, err := hex.DecodeString(string(x5tSHA256bytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err)
|
||||
return fmt.Errorf("go-jose/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err)
|
||||
}
|
||||
x5tSHA256bytes = hx256
|
||||
}
|
||||
@ -286,10 +304,10 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
x5tSHA1Len := len(k.CertificateThumbprintSHA1)
|
||||
x5tSHA256Len := len(k.CertificateThumbprintSHA256)
|
||||
if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size {
|
||||
return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t header is of incorrect size")
|
||||
}
|
||||
if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size {
|
||||
return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5t#S256 header is of incorrect size")
|
||||
}
|
||||
|
||||
// If certificate chain *and* thumbprints are set, verify correctness.
|
||||
@ -299,11 +317,11 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||
sha256sum := sha256.Sum256(leaf.Raw)
|
||||
|
||||
if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) {
|
||||
return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5c thumbprint does not match x5t value")
|
||||
}
|
||||
|
||||
if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) {
|
||||
return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value")
|
||||
return errors.New("go-jose/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value")
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +360,7 @@ func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
|
||||
}
|
||||
|
||||
if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength {
|
||||
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
|
||||
return "", errors.New("go-jose/go-jose: invalid elliptic key (too large)")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(ecThumbprintTemplate, crv,
|
||||
@ -359,7 +377,7 @@ func rsaThumbprintInput(n *big.Int, e int) (string, error) {
|
||||
func edThumbprintInput(ed ed25519.PublicKey) (string, error) {
|
||||
crv := "Ed25519"
|
||||
if len(ed) > 32 {
|
||||
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
|
||||
return "", errors.New("go-jose/go-jose: invalid elliptic key (too large)")
|
||||
}
|
||||
return fmt.Sprintf(edThumbprintTemplate, crv,
|
||||
newFixedSizeBuffer(ed, 32).base64()), nil
|
||||
@ -383,8 +401,10 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
input, err = rsaThumbprintInput(key.N, key.E)
|
||||
case ed25519.PrivateKey:
|
||||
input, err = edThumbprintInput(ed25519.PublicKey(key[32:]))
|
||||
case OpaqueSigner:
|
||||
return key.Public().Thumbprint(hash)
|
||||
default:
|
||||
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -392,7 +412,7 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
h.Write([]byte(input))
|
||||
_, _ = h.Write([]byte(input))
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
@ -463,7 +483,7 @@ func (k *JSONWebKey) Valid() bool {
|
||||
|
||||
func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) {
|
||||
if key.N == nil || key.E == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid RSA key, missing n/e values")
|
||||
}
|
||||
|
||||
return &rsa.PublicKey{
|
||||
@ -498,29 +518,29 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
|
||||
case "P-521":
|
||||
curve = elliptic.P521()
|
||||
default:
|
||||
return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||
}
|
||||
|
||||
if key.X == nil || key.Y == nil {
|
||||
return nil, errors.New("square/go-jose: invalid EC key, missing x/y values")
|
||||
return nil, errors.New("go-jose/go-jose: invalid EC key, missing x/y values")
|
||||
}
|
||||
|
||||
// The length of this octet string MUST be the full size of a coordinate for
|
||||
// the curve specified in the "crv" parameter.
|
||||
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
|
||||
if curveSize(curve) != len(key.X.data) {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for x")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC public key, wrong length for x")
|
||||
}
|
||||
|
||||
if curveSize(curve) != len(key.Y.data) {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for y")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC public key, wrong length for y")
|
||||
}
|
||||
|
||||
x := key.X.bigInt()
|
||||
y := key.Y.bigInt()
|
||||
|
||||
if !curve.IsOnCurve(x, y) {
|
||||
return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve")
|
||||
return nil, errors.New("go-jose/go-jose: invalid EC key, X/Y are not on declared curve")
|
||||
}
|
||||
|
||||
return &ecdsa.PublicKey{
|
||||
@ -532,7 +552,7 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
|
||||
|
||||
func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) {
|
||||
if pub == nil || pub.X == nil || pub.Y == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC key (nil, or X/Y missing)")
|
||||
}
|
||||
|
||||
name, err := curveName(pub.Curve)
|
||||
@ -546,7 +566,7 @@ func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) {
|
||||
yBytes := pub.Y.Bytes()
|
||||
|
||||
if len(xBytes) > size || len(yBytes) > size {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC key (X/Y too large)")
|
||||
}
|
||||
|
||||
key := &rawJSONWebKey{
|
||||
@ -569,7 +589,7 @@ func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) {
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", "))
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
privateKey := make([]byte, ed25519.PrivateKeySize)
|
||||
@ -581,7 +601,7 @@ func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) {
|
||||
|
||||
func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) {
|
||||
if key.X == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid Ed key, missing x value")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid Ed key, missing x value")
|
||||
}
|
||||
publicKey := make([]byte, ed25519.PublicKeySize)
|
||||
copy(publicKey[0:32], key.X.bytes())
|
||||
@ -605,7 +625,7 @@ func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", "))
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
rv := &rsa.PrivateKey{
|
||||
@ -675,34 +695,34 @@ func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) {
|
||||
case "P-521":
|
||||
curve = elliptic.P521()
|
||||
default:
|
||||
return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||
}
|
||||
|
||||
if key.X == nil || key.Y == nil || key.D == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, missing x/y/d values")
|
||||
}
|
||||
|
||||
// The length of this octet string MUST be the full size of a coordinate for
|
||||
// the curve specified in the "crv" parameter.
|
||||
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
|
||||
if curveSize(curve) != len(key.X.data) {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, wrong length for x")
|
||||
}
|
||||
|
||||
if curveSize(curve) != len(key.Y.data) {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, wrong length for y")
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7518#section-6.2.2.1
|
||||
if dSize(curve) != len(key.D.data) {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for d")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key, wrong length for d")
|
||||
}
|
||||
|
||||
x := key.X.bigInt()
|
||||
y := key.Y.bigInt()
|
||||
|
||||
if !curve.IsOnCurve(x, y) {
|
||||
return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve")
|
||||
return nil, errors.New("go-jose/go-jose: invalid EC key, X/Y are not on declared curve")
|
||||
}
|
||||
|
||||
return &ecdsa.PrivateKey{
|
||||
@ -722,7 +742,7 @@ func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJSONWebKey, error) {
|
||||
}
|
||||
|
||||
if ec.D == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC private key")
|
||||
}
|
||||
|
||||
raw.D = newFixedSizeBuffer(ec.D.Bytes(), dSize(ec.PublicKey.Curve))
|
||||
@ -740,7 +760,7 @@ func dSize(curve elliptic.Curve) int {
|
||||
bitLen := order.BitLen()
|
||||
size := bitLen / 8
|
||||
if bitLen%8 != 0 {
|
||||
size = size + 1
|
||||
size++
|
||||
}
|
||||
return size
|
||||
}
|
||||
@ -754,7 +774,50 @@ func fromSymmetricKey(key []byte) (*rawJSONWebKey, error) {
|
||||
|
||||
func (key rawJSONWebKey) symmetricKey() ([]byte, error) {
|
||||
if key.K == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid OCT (symmetric) key, missing k value")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid OCT (symmetric) key, missing k value")
|
||||
}
|
||||
return key.K.bytes(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrJWKSKidNotFound is returned when a JWKS does not contain a JWK with a
|
||||
// key ID which matches one in the provided tokens headers.
|
||||
ErrJWKSKidNotFound = errors.New("go-jose/go-jose: JWK with matching kid not found in JWK Set")
|
||||
)
|
||||
|
||||
func tryJWKS(key interface{}, headers ...Header) (interface{}, error) {
|
||||
var jwks JSONWebKeySet
|
||||
|
||||
switch jwksType := key.(type) {
|
||||
case *JSONWebKeySet:
|
||||
jwks = *jwksType
|
||||
case JSONWebKeySet:
|
||||
jwks = jwksType
|
||||
default:
|
||||
// If the specified key is not a JWKS, return as is.
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Determine the KID to search for from the headers.
|
||||
var kid string
|
||||
for _, header := range headers {
|
||||
if header.KeyID != "" {
|
||||
kid = header.KeyID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If no KID is specified in the headers, reject.
|
||||
if kid == "" {
|
||||
return nil, ErrJWKSKidNotFound
|
||||
}
|
||||
|
||||
// Find the JWK with the matching KID. If no JWK with the specified KID is
|
||||
// found, reject.
|
||||
keys := jwks.Key(kid)
|
||||
if len(keys) == 0 {
|
||||
return nil, ErrJWKSKidNotFound
|
||||
}
|
||||
|
||||
return keys[0].Key, nil
|
||||
}
|
103
vendor/gopkg.in/square/go-jose.v2/jws.go → vendor/github.com/go-jose/go-jose/v4/jws.go
generated
vendored
103
vendor/gopkg.in/square/go-jose.v2/jws.go → vendor/github.com/go-jose/go-jose/v4/jws.go
generated
vendored
@ -23,7 +23,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
|
||||
@ -75,22 +75,41 @@ type Signature struct {
|
||||
original *rawSignatureInfo
|
||||
}
|
||||
|
||||
// ParseSigned parses a signed message in compact or full serialization format.
|
||||
func ParseSigned(signature string) (*JSONWebSignature, error) {
|
||||
// ParseSigned parses a signed message in JWS Compact or JWS JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7
|
||||
func ParseSigned(
|
||||
signature string,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
signature = stripWhitespace(signature)
|
||||
if strings.HasPrefix(signature, "{") {
|
||||
return parseSignedFull(signature)
|
||||
return ParseSignedJSON(signature, signatureAlgorithms)
|
||||
}
|
||||
|
||||
return parseSignedCompact(signature, nil)
|
||||
return parseSignedCompact(signature, nil, signatureAlgorithms)
|
||||
}
|
||||
|
||||
// ParseSignedCompact parses a message in JWS Compact Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.1
|
||||
func ParseSignedCompact(
|
||||
signature string,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
return parseSignedCompact(signature, nil, signatureAlgorithms)
|
||||
}
|
||||
|
||||
// ParseDetached parses a signed message in compact serialization format with detached payload.
|
||||
func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) {
|
||||
func ParseDetached(
|
||||
signature string,
|
||||
payload []byte,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
if payload == nil {
|
||||
return nil, errors.New("square/go-jose: nil payload")
|
||||
return nil, errors.New("go-jose/go-jose: nil payload")
|
||||
}
|
||||
return parseSignedCompact(stripWhitespace(signature), payload)
|
||||
return parseSignedCompact(stripWhitespace(signature), payload, signatureAlgorithms)
|
||||
}
|
||||
|
||||
// Get a header value
|
||||
@ -137,21 +156,38 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature
|
||||
return authData.Bytes(), nil
|
||||
}
|
||||
|
||||
// parseSignedFull parses a message in full format.
|
||||
func parseSignedFull(input string) (*JSONWebSignature, error) {
|
||||
// ParseSignedJSON parses a message in JWS JSON Serialization.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7515#section-7.2
|
||||
func ParseSignedJSON(
|
||||
input string,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
var parsed rawJSONWebSignature
|
||||
err := json.Unmarshal([]byte(input), &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parsed.sanitized()
|
||||
return parsed.sanitized(signatureAlgorithms)
|
||||
}
|
||||
|
||||
func containsSignatureAlgorithm(haystack []SignatureAlgorithm, needle SignatureAlgorithm) bool {
|
||||
for _, algorithm := range haystack {
|
||||
if algorithm == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sanitized produces a cleaned-up JWS object from the raw JSON.
|
||||
func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgorithm) (*JSONWebSignature, error) {
|
||||
if len(signatureAlgorithms) == 0 {
|
||||
return nil, errors.New("go-jose/go-jose: no signature algorithms specified")
|
||||
}
|
||||
if parsed.Payload == nil {
|
||||
return nil, fmt.Errorf("square/go-jose: missing payload in JWS message")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: missing payload in JWS message")
|
||||
}
|
||||
|
||||
obj := &JSONWebSignature{
|
||||
@ -198,6 +234,12 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
alg := SignatureAlgorithm(signature.Header.Algorithm)
|
||||
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||
alg, signatureAlgorithms)
|
||||
}
|
||||
|
||||
if signature.header != nil {
|
||||
signature.Unprotected, err = signature.header.sanitized()
|
||||
if err != nil {
|
||||
@ -215,7 +257,7 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
|
||||
jwk := signature.Header.JSONWebKey
|
||||
if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
|
||||
return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
|
||||
return nil, errors.New("go-jose/go-jose: invalid embedded jwk, must be public key")
|
||||
}
|
||||
|
||||
obj.Signatures = append(obj.Signatures, signature)
|
||||
@ -241,6 +283,12 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
alg := SignatureAlgorithm(obj.Signatures[i].Header.Algorithm)
|
||||
if !containsSignatureAlgorithm(signatureAlgorithms, alg) {
|
||||
return nil, fmt.Errorf("go-jose/go-jose: unexpected signature algorithm %q; expected %q",
|
||||
alg, signatureAlgorithms)
|
||||
}
|
||||
|
||||
if obj.Signatures[i].header != nil {
|
||||
obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
|
||||
if err != nil {
|
||||
@ -260,7 +308,7 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
|
||||
jwk := obj.Signatures[i].Header.JSONWebKey
|
||||
if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
|
||||
return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
|
||||
return nil, errors.New("go-jose/go-jose: invalid embedded jwk, must be public key")
|
||||
}
|
||||
|
||||
// Copy value of sig
|
||||
@ -274,14 +322,18 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||
}
|
||||
|
||||
// parseSignedCompact parses a message in compact format.
|
||||
func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error) {
|
||||
func parseSignedCompact(
|
||||
input string,
|
||||
payload []byte,
|
||||
signatureAlgorithms []SignatureAlgorithm,
|
||||
) (*JSONWebSignature, error) {
|
||||
parts := strings.Split(input, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts")
|
||||
}
|
||||
|
||||
if parts[1] != "" && payload != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: payload is not detached")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: payload is not detached")
|
||||
}
|
||||
|
||||
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
@ -306,7 +358,7 @@ func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error)
|
||||
Protected: newBuffer(rawProtected),
|
||||
Signature: newBuffer(signature),
|
||||
}
|
||||
return raw.sanitized()
|
||||
return raw.sanitized(signatureAlgorithms)
|
||||
}
|
||||
|
||||
func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
||||
@ -314,15 +366,18 @@ func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
||||
return "", ErrNotSupported
|
||||
}
|
||||
|
||||
serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected))
|
||||
payload := ""
|
||||
signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)
|
||||
serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
|
||||
|
||||
var payload []byte
|
||||
if !detached {
|
||||
payload = base64.RawURLEncoding.EncodeToString(obj.payload)
|
||||
payload = obj.payload
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil
|
||||
return base64JoinWithDots(
|
||||
serializedProtected,
|
||||
payload,
|
||||
obj.Signatures[0].Signature,
|
||||
), nil
|
||||
}
|
||||
|
||||
// CompactSerialize serializes an object using the compact serialization format.
|
@ -83,6 +83,9 @@ func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg Sig
|
||||
}
|
||||
|
||||
// OpaqueKeyEncrypter is an interface that supports encrypting keys with an opaque key.
|
||||
//
|
||||
// Note: this cannot currently be implemented outside this package because of its
|
||||
// unexported method.
|
||||
type OpaqueKeyEncrypter interface {
|
||||
// KeyID returns the kid
|
||||
KeyID() string
|
||||
@ -121,7 +124,7 @@ func (oke *opaqueKeyEncrypter) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
|
||||
return oke.encrypter.encryptKey(cek, alg)
|
||||
}
|
||||
|
||||
//OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
|
||||
// OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
|
||||
type OpaqueKeyDecrypter interface {
|
||||
DecryptKey(encryptedKey []byte, header Header) ([]byte, error)
|
||||
}
|
@ -23,7 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// KeyAlgorithm represents a key management algorithm.
|
||||
@ -45,32 +45,38 @@ var (
|
||||
// ErrCryptoFailure represents an error in cryptographic primitive. This
|
||||
// occurs when, for example, a message had an invalid authentication tag or
|
||||
// could not be decrypted.
|
||||
ErrCryptoFailure = errors.New("square/go-jose: error in cryptographic primitive")
|
||||
ErrCryptoFailure = errors.New("go-jose/go-jose: error in cryptographic primitive")
|
||||
|
||||
// ErrUnsupportedAlgorithm indicates that a selected algorithm is not
|
||||
// supported. This occurs when trying to instantiate an encrypter for an
|
||||
// algorithm that is not yet implemented.
|
||||
ErrUnsupportedAlgorithm = errors.New("square/go-jose: unknown/unsupported algorithm")
|
||||
ErrUnsupportedAlgorithm = errors.New("go-jose/go-jose: unknown/unsupported algorithm")
|
||||
|
||||
// ErrUnsupportedKeyType indicates that the given key type/format is not
|
||||
// supported. This occurs when trying to instantiate an encrypter and passing
|
||||
// it a key of an unrecognized type or with unsupported parameters, such as
|
||||
// an RSA private key with more than two primes.
|
||||
ErrUnsupportedKeyType = errors.New("square/go-jose: unsupported key type/format")
|
||||
ErrUnsupportedKeyType = errors.New("go-jose/go-jose: unsupported key type/format")
|
||||
|
||||
// ErrInvalidKeySize indicates that the given key is not the correct size
|
||||
// for the selected algorithm. This can occur, for example, when trying to
|
||||
// encrypt with AES-256 but passing only a 128-bit key as input.
|
||||
ErrInvalidKeySize = errors.New("square/go-jose: invalid key size for algorithm")
|
||||
ErrInvalidKeySize = errors.New("go-jose/go-jose: invalid key size for algorithm")
|
||||
|
||||
// ErrNotSupported serialization of object is not supported. This occurs when
|
||||
// trying to compact-serialize an object which can't be represented in
|
||||
// compact form.
|
||||
ErrNotSupported = errors.New("square/go-jose: compact serialization not supported for object")
|
||||
ErrNotSupported = errors.New("go-jose/go-jose: compact serialization not supported for object")
|
||||
|
||||
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
|
||||
// nonce header parameter was included in an unprotected header object.
|
||||
ErrUnprotectedNonce = errors.New("square/go-jose: Nonce parameter included in unprotected header")
|
||||
ErrUnprotectedNonce = errors.New("go-jose/go-jose: Nonce parameter included in unprotected header")
|
||||
|
||||
// ErrMissingX5cHeader indicates that the JWT header is missing x5c headers.
|
||||
ErrMissingX5cHeader = errors.New("go-jose/go-jose: no x5c header present in message")
|
||||
|
||||
// ErrUnsupportedEllipticCurve indicates unsupported or unknown elliptic curve has been found.
|
||||
ErrUnsupportedEllipticCurve = errors.New("go-jose/go-jose: unsupported/unknown elliptic curve")
|
||||
)
|
||||
|
||||
// Key management algorithms
|
||||
@ -133,8 +139,8 @@ const (
|
||||
type HeaderKey string
|
||||
|
||||
const (
|
||||
HeaderType HeaderKey = "typ" // string
|
||||
HeaderContentType = "cty" // string
|
||||
HeaderType = "typ" // string
|
||||
HeaderContentType = "cty" // string
|
||||
|
||||
// These are set by go-jose and shouldn't need to be set by consumers of the
|
||||
// library.
|
||||
@ -183,8 +189,13 @@ type Header struct {
|
||||
// Unverified certificate chain parsed from x5c header.
|
||||
certificates []*x509.Certificate
|
||||
|
||||
// Any headers not recognised above get unmarshalled
|
||||
// from JSON in a generic manner and placed in this map.
|
||||
// At parse time, each header parameter with a name other than "kid",
|
||||
// "jwk", "alg", "nonce", or "x5c" will have its value passed to
|
||||
// [json.Unmarshal] to unmarshal it into an interface value.
|
||||
// The resulting value will be stored in this map, with the header
|
||||
// parameter name as the key.
|
||||
//
|
||||
// [json.Unmarshal]: https://pkg.go.dev/encoding/json#Unmarshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
||||
@ -194,7 +205,7 @@ type Header struct {
|
||||
// not be validated with the given verify options.
|
||||
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
|
||||
if len(h.certificates) == 0 {
|
||||
return nil, errors.New("square/go-jose: no x5c header present in message")
|
||||
return nil, ErrMissingX5cHeader
|
||||
}
|
||||
|
||||
leaf := h.certificates[0]
|
||||
@ -452,8 +463,8 @@ func parseCertificateChain(chain []string) ([]*x509.Certificate, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (dst rawHeader) isSet(k HeaderKey) bool {
|
||||
dvr := dst[k]
|
||||
func (parsed rawHeader) isSet(k HeaderKey) bool {
|
||||
dvr := parsed[k]
|
||||
if dvr == nil {
|
||||
return false
|
||||
}
|
||||
@ -472,17 +483,17 @@ func (dst rawHeader) isSet(k HeaderKey) bool {
|
||||
}
|
||||
|
||||
// Merge headers from src into dst, giving precedence to headers from l.
|
||||
func (dst rawHeader) merge(src *rawHeader) {
|
||||
func (parsed rawHeader) merge(src *rawHeader) {
|
||||
if src == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range *src {
|
||||
if dst.isSet(k) {
|
||||
if parsed.isSet(k) {
|
||||
continue
|
||||
}
|
||||
|
||||
dst[k] = v
|
||||
parsed[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,7 +507,7 @@ func curveName(crv elliptic.Curve) (string, error) {
|
||||
case elliptic.P521():
|
||||
return "P-521", nil
|
||||
default:
|
||||
return "", fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve")
|
||||
return "", ErrUnsupportedEllipticCurve
|
||||
}
|
||||
}
|
||||
|
@ -19,14 +19,13 @@ package jose
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/json"
|
||||
"github.com/go-jose/go-jose/v4/json"
|
||||
)
|
||||
|
||||
// NonceSource represents a source of random nonces to go into JWS objects
|
||||
@ -41,6 +40,20 @@ type Signer interface {
|
||||
}
|
||||
|
||||
// SigningKey represents an algorithm/key used to sign a message.
|
||||
//
|
||||
// Key must have one of these types:
|
||||
// - ed25519.PrivateKey
|
||||
// - *ecdsa.PrivateKey
|
||||
// - *rsa.PrivateKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - []byte (an HMAC key)
|
||||
// - Any type that satisfies the OpaqueSigner interface
|
||||
//
|
||||
// If the key is an HMAC key, it must have at least as many bytes as the relevant hash output:
|
||||
// - HS256: 32 bytes
|
||||
// - HS384: 48 bytes
|
||||
// - HS512: 64 bytes
|
||||
type SigningKey struct {
|
||||
Algorithm SignatureAlgorithm
|
||||
Key interface{}
|
||||
@ -53,12 +66,22 @@ type SignerOptions struct {
|
||||
|
||||
// Optional map of additional keys to be inserted into the protected header
|
||||
// of a JWS object. Some specifications which make use of JWS like to insert
|
||||
// additional values here. All values must be JSON-serializable.
|
||||
// additional values here.
|
||||
//
|
||||
// Values will be serialized by [json.Marshal] and must be valid inputs to
|
||||
// that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
ExtraHeaders map[HeaderKey]interface{}
|
||||
}
|
||||
|
||||
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
|
||||
// if necessary. It returns itself and so can be used in a fluent style.
|
||||
// if necessary, and returns the updated SignerOptions.
|
||||
//
|
||||
// The v argument will be serialized by [json.Marshal] and must be a valid
|
||||
// input to that function.
|
||||
//
|
||||
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
|
||||
func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions {
|
||||
if so.ExtraHeaders == nil {
|
||||
so.ExtraHeaders = map[HeaderKey]interface{}{}
|
||||
@ -174,11 +197,11 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
|
||||
return newVerifier(verificationKey.Key)
|
||||
case *JSONWebKey:
|
||||
return newVerifier(verificationKey.Key)
|
||||
case OpaqueVerifier:
|
||||
return &opaqueVerifier{verifier: verificationKey}, nil
|
||||
default:
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
if ov, ok := verificationKey.(OpaqueVerifier); ok {
|
||||
return &opaqueVerifier{verifier: ov}, nil
|
||||
}
|
||||
return nil, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
|
||||
@ -205,11 +228,11 @@ func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipient
|
||||
return newJWKSigner(alg, signingKey)
|
||||
case *JSONWebKey:
|
||||
return newJWKSigner(alg, *signingKey)
|
||||
case OpaqueSigner:
|
||||
return newOpaqueSigner(alg, signingKey)
|
||||
default:
|
||||
return recipientSigInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
if signer, ok := signingKey.(OpaqueSigner); ok {
|
||||
return newOpaqueSigner(alg, signer)
|
||||
}
|
||||
return recipientSigInfo{}, ErrUnsupportedKeyType
|
||||
}
|
||||
|
||||
func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) {
|
||||
@ -227,7 +250,7 @@ func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigIn
|
||||
|
||||
// This should be impossible, but let's check anyway.
|
||||
if !recipient.publicKey().IsPublic() {
|
||||
return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public")
|
||||
return recipientSigInfo{}, errors.New("go-jose/go-jose: public key was unexpectedly not public")
|
||||
}
|
||||
}
|
||||
return recipient, nil
|
||||
@ -251,7 +274,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||
// result of the JOSE spec. We've decided that this library will only include one or
|
||||
// the other to avoid this confusion.
|
||||
//
|
||||
// See https://github.com/square/go-jose/issues/157 for more context.
|
||||
// See https://github.com/go-jose/go-jose/issues/157 for more context.
|
||||
if ctx.embedJWK {
|
||||
protected[headerJWK] = recipient.publicKey()
|
||||
} else {
|
||||
@ -265,7 +288,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||
if ctx.nonceSource != nil {
|
||||
nonce, err := ctx.nonceSource.Nonce()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: Error generating nonce: %v", err)
|
||||
}
|
||||
protected[headerNonce] = nonce
|
||||
}
|
||||
@ -279,7 +302,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||
|
||||
if b64, ok := protected[headerB64]; ok {
|
||||
if needsBase64, ok = b64.(bool); !ok {
|
||||
return nil, errors.New("square/go-jose: Invalid b64 header parameter")
|
||||
return nil, errors.New("go-jose/go-jose: Invalid b64 header parameter")
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +326,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||
for k, v := range protected {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: Error marshalling item %#v: %v", k, err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: Error marshalling item %#v: %v", k, err)
|
||||
}
|
||||
(*signatureInfo.protected)[k] = makeRawMessage(b)
|
||||
}
|
||||
@ -322,12 +345,28 @@ func (ctx *genericSigner) Options() SignerOptions {
|
||||
}
|
||||
|
||||
// Verify validates the signature on the object and returns the payload.
|
||||
// This function does not support multi-signature, if you desire multi-sig
|
||||
// This function does not support multi-signature. If you desire multi-signature
|
||||
// verification use VerifyMulti instead.
|
||||
//
|
||||
// Be careful when verifying signatures based on embedded JWKs inside the
|
||||
// payload header. You cannot assume that the key received in a payload is
|
||||
// trusted.
|
||||
//
|
||||
// The verificationKey argument must have one of these types:
|
||||
// - ed25519.PublicKey
|
||||
// - *ecdsa.PublicKey
|
||||
// - *rsa.PublicKey
|
||||
// - *JSONWebKey
|
||||
// - JSONWebKey
|
||||
// - *JSONWebKeySet
|
||||
// - JSONWebKeySet
|
||||
// - []byte (an HMAC key)
|
||||
// - Any type that implements the OpaqueVerifier interface.
|
||||
//
|
||||
// If the key is an HMAC key, it must have at least as many bytes as the relevant hash output:
|
||||
// - HS256: 32 bytes
|
||||
// - HS384: 48 bytes
|
||||
// - HS512: 64 bytes
|
||||
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
|
||||
err := obj.DetachedVerify(obj.payload, verificationKey)
|
||||
if err != nil {
|
||||
@ -347,14 +386,21 @@ func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
|
||||
// most cases, you will probably want to use Verify instead. DetachedVerify
|
||||
// is only useful if you have a payload and signature that are separated from
|
||||
// each other.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
|
||||
verifier, err := newVerifier(verificationKey)
|
||||
key, err := tryJWKS(verificationKey, obj.headers()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verifier, err := newVerifier(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(obj.Signatures) > 1 {
|
||||
return errors.New("square/go-jose: too many signatures in payload; expecting only one")
|
||||
return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one")
|
||||
}
|
||||
|
||||
signature := obj.Signatures[0]
|
||||
@ -388,6 +434,9 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter
|
||||
// returns the index of the signature that was verified, along with the signature
|
||||
// object and the payload. We return the signature and index to guarantee that
|
||||
// callers are getting the verified value.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
|
||||
idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
|
||||
if err != nil {
|
||||
@ -405,8 +454,15 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
|
||||
// DetachedVerifyMulti is only useful if you have a payload and signature that are
|
||||
// separated from each other, and the signature can have multiple signers at the
|
||||
// same time.
|
||||
//
|
||||
// The verificationKey argument must have one of the types allowed for the
|
||||
// verificationKey argument of JSONWebSignature.Verify().
|
||||
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
|
||||
verifier, err := newVerifier(verificationKey)
|
||||
key, err := tryJWKS(verificationKey, obj.headers()...)
|
||||
if err != nil {
|
||||
return -1, Signature{}, err
|
||||
}
|
||||
verifier, err := newVerifier(key)
|
||||
if err != nil {
|
||||
return -1, Signature{}, err
|
||||
}
|
||||
@ -439,3 +495,11 @@ outer:
|
||||
|
||||
return -1, Signature{}, ErrCryptoFailure
|
||||
}
|
||||
|
||||
func (obj JSONWebSignature) headers() []Header {
|
||||
headers := make([]Header, len(obj.Signatures))
|
||||
for i, sig := range obj.Signatures {
|
||||
headers[i] = sig.Header
|
||||
}
|
||||
return headers
|
||||
}
|
@ -31,20 +31,26 @@ import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"gopkg.in/square/go-jose.v2/cipher"
|
||||
|
||||
josecipher "github.com/go-jose/go-jose/v4/cipher"
|
||||
)
|
||||
|
||||
// Random reader (stubbed out in tests)
|
||||
// RandReader is a cryptographically secure random number generator (stubbed out in tests).
|
||||
var RandReader = rand.Reader
|
||||
|
||||
const (
|
||||
// RFC7518 recommends a minimum of 1,000 iterations:
|
||||
// https://tools.ietf.org/html/rfc7518#section-4.8.1.2
|
||||
// - https://tools.ietf.org/html/rfc7518#section-4.8.1.2
|
||||
//
|
||||
// NIST recommends a minimum of 10,000:
|
||||
// https://pages.nist.gov/800-63-3/sp800-63b.html
|
||||
// 1Password uses 100,000:
|
||||
// https://support.1password.com/pbkdf2/
|
||||
defaultP2C = 100000
|
||||
// - https://pages.nist.gov/800-63-3/sp800-63b.html
|
||||
//
|
||||
// 1Password increased in 2023 from 100,000 to 650,000:
|
||||
// - https://support.1password.com/pbkdf2/
|
||||
//
|
||||
// OWASP recommended 600,000 in Dec 2022:
|
||||
// - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
|
||||
defaultP2C = 600000
|
||||
// Default salt size: 128 bits
|
||||
defaultP2SSize = 16
|
||||
)
|
||||
@ -278,8 +284,14 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
|
||||
}
|
||||
|
||||
header := &rawHeader{}
|
||||
header.set(headerIV, newBuffer(parts.iv))
|
||||
header.set(headerTag, newBuffer(parts.tag))
|
||||
|
||||
if err = header.set(headerIV, newBuffer(parts.iv)); err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
if err = header.set(headerTag, newBuffer(parts.tag)); err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
return recipientInfo{
|
||||
header: header,
|
||||
@ -332,8 +344,14 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
|
||||
}
|
||||
|
||||
header := &rawHeader{}
|
||||
header.set(headerP2C, ctx.p2c)
|
||||
header.set(headerP2S, newBuffer(ctx.p2s))
|
||||
|
||||
if err = header.set(headerP2C, ctx.p2c); err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
if err = header.set(headerP2S, newBuffer(ctx.p2s)); err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
return recipientInfo{
|
||||
encryptedKey: jek,
|
||||
@ -356,11 +374,11 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
|
||||
iv, err := headers.getIV()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid IV: %v", err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid IV: %v", err)
|
||||
}
|
||||
tag, err := headers.getTag()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid tag: %v", err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid tag: %v", err)
|
||||
}
|
||||
|
||||
parts := &aeadParts{
|
||||
@ -389,18 +407,23 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
|
||||
p2s, err := headers.getP2S()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid P2S: %v", err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2S: %v", err)
|
||||
}
|
||||
if p2s == nil || len(p2s.data) == 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid P2S: must be present")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2S: must be present")
|
||||
}
|
||||
|
||||
p2c, err := headers.getP2C()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid P2C: %v", err)
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: %v", err)
|
||||
}
|
||||
if p2c <= 0 {
|
||||
return nil, fmt.Errorf("square/go-jose: invalid P2C: must be a positive integer")
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: must be a positive integer")
|
||||
}
|
||||
if p2c > 1000000 {
|
||||
// An unauthenticated attacker can set a high P2C value. Set an upper limit to avoid
|
||||
// DoS attacks.
|
||||
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: too high")
|
||||
}
|
||||
|
||||
// salt is UTF8(Alg) || 0x00 || Salt Input
|
||||
@ -431,7 +454,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
|
||||
func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||
mac, err := ctx.hmac(payload, alg)
|
||||
if err != nil {
|
||||
return Signature{}, errors.New("square/go-jose: failed to compute hmac")
|
||||
return Signature{}, err
|
||||
}
|
||||
|
||||
return Signature{
|
||||
@ -444,16 +467,16 @@ func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Sig
|
||||
func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error {
|
||||
expected, err := ctx.hmac(payload, alg)
|
||||
if err != nil {
|
||||
return errors.New("square/go-jose: failed to compute hmac")
|
||||
return errors.New("go-jose/go-jose: failed to compute hmac")
|
||||
}
|
||||
|
||||
if len(mac) != len(expected) {
|
||||
return errors.New("square/go-jose: invalid hmac")
|
||||
return errors.New("go-jose/go-jose: invalid hmac")
|
||||
}
|
||||
|
||||
match := subtle.ConstantTimeCompare(mac, expected)
|
||||
if match != 1 {
|
||||
return errors.New("square/go-jose: invalid hmac")
|
||||
return errors.New("go-jose/go-jose: invalid hmac")
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -463,12 +486,24 @@ func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureA
|
||||
func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
|
||||
var hash func() hash.Hash
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.2
|
||||
// A key of the same size as the hash output (for instance, 256 bits for
|
||||
// "HS256") or larger MUST be used
|
||||
switch alg {
|
||||
case HS256:
|
||||
if len(ctx.key)*8 < 256 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha256.New
|
||||
case HS384:
|
||||
if len(ctx.key)*8 < 384 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha512.New384
|
||||
case HS512:
|
||||
if len(ctx.key)*8 < 512 {
|
||||
return nil, ErrInvalidKeySize
|
||||
}
|
||||
hash = sha512.New
|
||||
default:
|
||||
return nil, ErrUnsupportedAlgorithm
|
11
vendor/github.com/goccy/go-json/.golangci.yml
generated
vendored
11
vendor/github.com/goccy/go-json/.golangci.yml
generated
vendored
@ -48,6 +48,17 @@ linters:
|
||||
- nlreturn
|
||||
- testpackage
|
||||
- wsl
|
||||
- varnamelen
|
||||
- nilnil
|
||||
- ireturn
|
||||
- govet
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
- containedctx
|
||||
- revive
|
||||
- nosnakecase
|
||||
- exhaustruct
|
||||
- depguard
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
|
165
vendor/github.com/goccy/go-json/CHANGELOG.md
generated
vendored
165
vendor/github.com/goccy/go-json/CHANGELOG.md
generated
vendored
@ -1,3 +1,168 @@
|
||||
# v0.10.2 - 2023/03/20
|
||||
|
||||
### New features
|
||||
|
||||
* Support DebugDOT option for debugging encoder ( #440 )
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix combination of embedding structure and omitempty option ( #442 )
|
||||
|
||||
# v0.10.1 - 2023/03/13
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix checkptr error for array decoder ( #415 )
|
||||
* Fix added buffer size check when decoding key ( #430 )
|
||||
* Fix handling of anonymous fields other than struct ( #431 )
|
||||
* Fix to not optimize when lower conversion can't handle byte-by-byte ( #432 )
|
||||
* Fix a problem that MarshalIndent does not work when UnorderedMap is specified ( #435 )
|
||||
* Fix mapDecoder.DecodeStream() for empty objects containing whitespace ( #425 )
|
||||
* Fix an issue that could not set the correct NextField for fields in the embedded structure ( #438 )
|
||||
|
||||
# v0.10.0 - 2022/11/29
|
||||
|
||||
### New features
|
||||
|
||||
* Support JSON Path ( #250 )
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix marshaler for map's key ( #409 )
|
||||
|
||||
# v0.9.11 - 2022/08/18
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix unexpected behavior when buffer ends with backslash ( #383 )
|
||||
* Fix stream decoding of escaped character ( #387 )
|
||||
|
||||
# v0.9.10 - 2022/07/15
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix boundary exception of type caching ( #382 )
|
||||
|
||||
# v0.9.9 - 2022/07/15
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix encoding of directed interface with typed nil ( #377 )
|
||||
* Fix embedded primitive type encoding using alias ( #378 )
|
||||
* Fix slice/array type encoding with types implementing MarshalJSON ( #379 )
|
||||
* Fix unicode decoding when the expected buffer state is not met after reading ( #380 )
|
||||
|
||||
# v0.9.8 - 2022/06/30
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix decoding of surrogate-pair ( #365 )
|
||||
* Fix handling of embedded primitive type ( #366 )
|
||||
* Add validation of escape sequence for decoder ( #367 )
|
||||
* Fix stream tokenizing respecting UseNumber ( #369 )
|
||||
* Fix encoding when struct pointer type that implements Marshal JSON is embedded ( #375 )
|
||||
|
||||
### Improve performance
|
||||
|
||||
* Improve performance of linkRecursiveCode ( #368 )
|
||||
|
||||
# v0.9.7 - 2022/04/22
|
||||
|
||||
### Fix bugs
|
||||
|
||||
#### Encoder
|
||||
|
||||
* Add filtering process for encoding on slow path ( #355 )
|
||||
* Fix encoding of interface{} with pointer type ( #363 )
|
||||
|
||||
#### Decoder
|
||||
|
||||
* Fix map key decoder that implements UnmarshalJSON ( #353 )
|
||||
* Fix decoding of []uint8 type ( #361 )
|
||||
|
||||
### New features
|
||||
|
||||
* Add DebugWith option for encoder ( #356 )
|
||||
|
||||
# v0.9.6 - 2022/03/22
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Correct the handling of the minimum value of int type for decoder ( #344 )
|
||||
* Fix bugs of stream decoder's bufferSize ( #349 )
|
||||
* Add a guard to use typeptr more safely ( #351 )
|
||||
|
||||
### Improve decoder performance
|
||||
|
||||
* Improve escapeString's performance ( #345 )
|
||||
|
||||
### Others
|
||||
|
||||
* Update go version for CI ( #347 )
|
||||
|
||||
# v0.9.5 - 2022/03/04
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix panic when decoding time.Time with context ( #328 )
|
||||
* Fix reading the next character in buffer to nul consideration ( #338 )
|
||||
* Fix incorrect handling on skipValue ( #341 )
|
||||
|
||||
### Improve decoder performance
|
||||
|
||||
* Improve performance when a payload contains escape sequence ( #334 )
|
||||
|
||||
# v0.9.4 - 2022/01/21
|
||||
|
||||
* Fix IsNilForMarshaler for string type with omitempty ( #323 )
|
||||
* Fix the case where the embedded field is at the end ( #326 )
|
||||
|
||||
# v0.9.3 - 2022/01/14
|
||||
|
||||
* Fix logic of removing struct field for decoder ( #322 )
|
||||
|
||||
# v0.9.2 - 2022/01/14
|
||||
|
||||
* Add invalid decoder to delay type error judgment at decode ( #321 )
|
||||
|
||||
# v0.9.1 - 2022/01/11
|
||||
|
||||
* Fix encoding of MarshalText/MarshalJSON operation with head offset ( #319 )
|
||||
|
||||
# v0.9.0 - 2022/01/05
|
||||
|
||||
### New feature
|
||||
|
||||
* Supports dynamic filtering of struct fields ( #314 )
|
||||
|
||||
### Improve encoding performance
|
||||
|
||||
* Improve map encoding performance ( #310 )
|
||||
* Optimize encoding path for escaped string ( #311 )
|
||||
* Add encoding option for performance ( #312 )
|
||||
|
||||
### Fix bugs
|
||||
|
||||
* Fix panic at encoding map value on 1.18 ( #310 )
|
||||
* Fix MarshalIndent for interface type ( #317 )
|
||||
|
||||
# v0.8.1 - 2021/12/05
|
||||
|
||||
* Fix operation conversion from PtrHead to Head in Recursive type ( #305 )
|
||||
|
||||
# v0.8.0 - 2021/12/02
|
||||
|
||||
* Fix embedded field conflict behavior ( #300 )
|
||||
* Refactor compiler for encoder ( #301 #302 )
|
||||
|
||||
# v0.7.10 - 2021/10/16
|
||||
|
||||
* Fix conversion from pointer to uint64 ( #294 )
|
||||
|
||||
# v0.7.9 - 2021/09/28
|
||||
|
||||
* Fix encoding of nil value about interface type that has method ( #291 )
|
||||
|
||||
# v0.7.8 - 2021/09/01
|
||||
|
||||
* Fix mapassign_faststr for indirect struct type ( #283 )
|
||||
|
4
vendor/github.com/goccy/go-json/Makefile
generated
vendored
4
vendor/github.com/goccy/go-json/Makefile
generated
vendored
@ -22,7 +22,7 @@ cover-html: cover
|
||||
|
||||
.PHONY: lint
|
||||
lint: golangci-lint
|
||||
golangci-lint run
|
||||
$(BIN_DIR)/golangci-lint run
|
||||
|
||||
golangci-lint: | $(BIN_DIR)
|
||||
@{ \
|
||||
@ -30,7 +30,7 @@ golangci-lint: | $(BIN_DIR)
|
||||
GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \
|
||||
cd $$GOLANGCI_LINT_TMP_DIR; \
|
||||
go mod init tmp; \
|
||||
GOBIN=$(BIN_DIR) go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0; \
|
||||
GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2; \
|
||||
rm -rf $$GOLANGCI_LINT_TMP_DIR; \
|
||||
}
|
||||
|
||||
|
8
vendor/github.com/goccy/go-json/README.md
generated
vendored
8
vendor/github.com/goccy/go-json/README.md
generated
vendored
@ -13,7 +13,7 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
|
||||
```
|
||||
* version ( expected release date )
|
||||
|
||||
* v0.7.0
|
||||
* v0.9.0
|
||||
|
|
||||
| while maintaining compatibility with encoding/json, we will add convenient APIs
|
||||
|
|
||||
@ -21,9 +21,8 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
|
||||
* v1.0.0
|
||||
```
|
||||
|
||||
We are accepting requests for features that will be implemented between v0.7.0 and v.1.0.0.
|
||||
We are accepting requests for features that will be implemented between v0.9.0 and v.1.0.0.
|
||||
If you have the API you need, please submit your issue [here](https://github.com/goccy/go-json/issues).
|
||||
For example, I'm thinking of supporting `context.Context` of `json.Marshaler` and decoding using JSON Path.
|
||||
|
||||
# Features
|
||||
|
||||
@ -32,6 +31,7 @@ For example, I'm thinking of supporting `context.Context` of `json.Marshaler` an
|
||||
- Flexible customization with options
|
||||
- Coloring the encoded string
|
||||
- Can propagate context.Context to `MarshalJSON` or `UnmarshalJSON`
|
||||
- Can dynamically filter the fields of the structure type-safely
|
||||
|
||||
# Installation
|
||||
|
||||
@ -184,7 +184,7 @@ func Marshal(v interface{}) ([]byte, error) {
|
||||
`json.Marshal` and `json.Unmarshal` receive `interface{}` value and they perform type determination dynamically to process.
|
||||
In normal case, you need to use the `reflect` library to determine the type dynamically, but since `reflect.Type` is defined as `interface`, when you call the method of `reflect.Type`, The reflect's argument is escaped.
|
||||
|
||||
Therefore, the arguments for `Marshal` and `Unmarshal` are always escape to the heap.
|
||||
Therefore, the arguments for `Marshal` and `Unmarshal` are always escaped to the heap.
|
||||
However, `go-json` can use the feature of `reflect.Type` while avoiding escaping.
|
||||
|
||||
`reflect.Type` is defined as `interface`, but in reality `reflect.Type` is implemented only by the structure `rtype` defined in the `reflect` package.
|
||||
|
31
vendor/github.com/goccy/go-json/decode.go
generated
vendored
31
vendor/github.com/goccy/go-json/decode.go
generated
vendored
@ -83,6 +83,37 @@ func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs
|
||||
return validateEndBuf(src, cursor)
|
||||
}
|
||||
|
||||
var (
|
||||
pathDecoder = decoder.NewPathDecoder()
|
||||
)
|
||||
|
||||
func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
|
||||
if path.path.RootSelectorOnly {
|
||||
return [][]byte{data}, nil
|
||||
}
|
||||
src := make([]byte, len(data)+1) // append nul byte to the end
|
||||
copy(src, data)
|
||||
|
||||
ctx := decoder.TakeRuntimeContext()
|
||||
ctx.Buf = src
|
||||
ctx.Option.Flags = 0
|
||||
ctx.Option.Flags |= decoder.PathOption
|
||||
ctx.Option.Path = path.path
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
paths, cursor, err := pathDecoder.DecodePath(ctx, 0, 0)
|
||||
if err != nil {
|
||||
decoder.ReleaseRuntimeContext(ctx)
|
||||
return nil, err
|
||||
}
|
||||
decoder.ReleaseRuntimeContext(ctx)
|
||||
if err := validateEndBuf(src, cursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
||||
src := make([]byte, len(data)+1) // append nul byte to the end
|
||||
copy(src, data)
|
||||
|
2
vendor/github.com/goccy/go-json/docker-compose.yml
generated
vendored
2
vendor/github.com/goccy/go-json/docker-compose.yml
generated
vendored
@ -1,7 +1,7 @@
|
||||
version: '2'
|
||||
services:
|
||||
go-json:
|
||||
image: golang:1.16
|
||||
image: golang:1.18
|
||||
volumes:
|
||||
- '.:/go/src/go-json'
|
||||
deploy:
|
||||
|
21
vendor/github.com/goccy/go-json/encode.go
generated
vendored
21
vendor/github.com/goccy/go-json/encode.go
generated
vendored
@ -3,6 +3,7 @@ package json
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
@ -51,7 +52,7 @@ func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ...
|
||||
rctx.Option.Flag |= encoder.ContextOption
|
||||
rctx.Option.Context = ctx
|
||||
|
||||
err := e.encodeWithOption(rctx, v, optFuncs...)
|
||||
err := e.encodeWithOption(rctx, v, optFuncs...) //nolint: contextcheck
|
||||
|
||||
encoder.ReleaseRuntimeContext(rctx)
|
||||
return err
|
||||
@ -61,6 +62,8 @@ func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, o
|
||||
if e.enabledHTMLEscape {
|
||||
ctx.Option.Flag |= encoder.HTMLEscapeOption
|
||||
}
|
||||
ctx.Option.Flag |= encoder.NormalizeUTF8Option
|
||||
ctx.Option.DebugOut = os.Stdout
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
@ -111,13 +114,13 @@ func (e *Encoder) SetIndent(prefix, indent string) {
|
||||
func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||
rctx := encoder.TakeRuntimeContext()
|
||||
rctx.Option.Flag = 0
|
||||
rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.ContextOption
|
||||
rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.ContextOption
|
||||
rctx.Option.Context = ctx
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(rctx.Option)
|
||||
}
|
||||
|
||||
buf, err := encode(rctx, v)
|
||||
buf, err := encode(rctx, v) //nolint: contextcheck
|
||||
if err != nil {
|
||||
encoder.ReleaseRuntimeContext(rctx)
|
||||
return nil, err
|
||||
@ -139,7 +142,7 @@ func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||
ctx := encoder.TakeRuntimeContext()
|
||||
|
||||
ctx.Option.Flag = 0
|
||||
ctx.Option.Flag |= encoder.HTMLEscapeOption
|
||||
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
@ -166,7 +169,7 @@ func marshalNoEscape(v interface{}) ([]byte, error) {
|
||||
ctx := encoder.TakeRuntimeContext()
|
||||
|
||||
ctx.Option.Flag = 0
|
||||
ctx.Option.Flag |= encoder.HTMLEscapeOption
|
||||
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
|
||||
|
||||
buf, err := encodeNoEscape(ctx, v)
|
||||
if err != nil {
|
||||
@ -190,7 +193,7 @@ func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptio
|
||||
ctx := encoder.TakeRuntimeContext()
|
||||
|
||||
ctx.Option.Flag = 0
|
||||
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.IndentOption)
|
||||
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.IndentOption)
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
@ -220,7 +223,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -248,7 +251,7 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error)
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -275,7 +278,7 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
2
vendor/github.com/goccy/go-json/error.go
generated
vendored
2
vendor/github.com/goccy/go-json/error.go
generated
vendored
@ -37,3 +37,5 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
|
||||
type UnsupportedTypeError = errors.UnsupportedTypeError
|
||||
|
||||
type UnsupportedValueError = errors.UnsupportedValueError
|
||||
|
||||
type PathError = errors.PathError
|
||||
|
4
vendor/github.com/goccy/go-json/internal/decoder/anonymous_field.go
generated
vendored
4
vendor/github.com/goccy/go-json/internal/decoder/anonymous_field.go
generated
vendored
@ -35,3 +35,7 @@ func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
|
||||
p = *(*unsafe.Pointer)(p)
|
||||
return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return d.dec.DecodePath(ctx, cursor, depth)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user