woodpecker/vendor/github.com/go-ap/httpsig/common.go
Anbraten cc30db44ac
Use asym key to sign webhooks (#916)
* use async key pair for webhooks

* fix tests

* fix linter

* improve code

* add key pair to database

* undo some changes

* more undo

* improve docs

* add api-endpoint

* add signaturne api endpoint

* fix error

* fix linting and test

* fix lint

* add test

* migration 006

* no need for migration

* replace httsign lib

* fix lint

Co-authored-by: 6543 <6543@obermui.de>
2022-06-01 20:06:27 +02:00

141 lines
3.4 KiB
Go

// Copyright (C) 2017 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httpsig
import (
ed "crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"fmt"
"io"
"net/http"
"strings"
"time"
)
var (
// Rand is a hookable reader used as a random byte source.
Rand io.Reader = rand.Reader
)
// requestPath returns the :path pseudo header according to the HTTP/2 spec.
func requestPath(req *http.Request) string {
path := req.URL.Path
if path == "" {
path = "/"
}
if req.URL.RawQuery != "" {
path += "?" + req.URL.RawQuery
}
return path
}
// BuildSignatureString constructs a signature string following section 2.3
func BuildSignatureString(req *http.Request, headers []string, created, expires time.Time) (string, error) {
if len(headers) == 0 {
headers = []string{"(created)"}
}
values := make([]string, 0, len(headers))
for _, h := range headers {
switch h {
case "(request-target)":
values = append(values, fmt.Sprintf("%s: %s %s",
h, strings.ToLower(req.Method), requestPath(req)))
case "(created)":
values = append(values, fmt.Sprintf("%s: %d", h, created.Unix()))
case "(expires)":
values = append(values, fmt.Sprintf("%s: %d", h, expires.Unix()))
case "host":
values = append(values, fmt.Sprintf("%s: %s", h, req.Host))
case "date":
if req.Header.Get(h) == "" {
req.Header.Set(h, time.Now().UTC().Format(http.TimeFormat))
}
values = append(values, fmt.Sprintf("%s: %s", h, req.Header.Get(h)))
default:
vs, found := req.Header[http.CanonicalHeaderKey(h)]
if !found {
return "", fmt.Errorf("expected %s to exists", h)
}
for _, v := range vs {
values = append(values, fmt.Sprintf("%s: %s", h, strings.TrimSpace(v)))
}
}
}
return strings.Join(values, "\n"), nil
}
// BuildSignatureData is a convenience wrapper around BuildSignatureString that
// returns []byte instead of a string.
func BuildSignatureData(req *http.Request, headers []string, created, expires time.Time) ([]byte, error) {
s, err := BuildSignatureString(req, headers, created, expires)
return []byte(s), err
}
func toRSAPrivateKey(key interface{}) *rsa.PrivateKey {
switch k := key.(type) {
case *rsa.PrivateKey:
return k
default:
return nil
}
}
func toRSAPublicKey(key interface{}) *rsa.PublicKey {
switch k := key.(type) {
case *rsa.PublicKey:
return k
case *rsa.PrivateKey:
return &k.PublicKey
default:
return nil
}
}
func toHMACKey(key interface{}) []byte {
switch k := key.(type) {
case []byte:
return k
default:
return nil
}
}
func toEd25519PrivateKey(key interface{}) ed.PrivateKey {
switch k := key.(type) {
case ed.PrivateKey:
return k
default:
return nil
}
}
func toEd25519PublicKey(key interface{}) ed.PublicKey {
switch k := key.(type) {
case ed.PublicKey:
return k
case ed.PrivateKey:
return k.Public().(ed.PublicKey)
default:
return nil
}
}
func unsupportedAlgorithm(a Algorithm) error {
return fmt.Errorf("key does not support algorithm %q", a.Name())
}