(except for iceshrimp, but who cares) (well, I do. Would not be nice to not be compatible with a not-so-rarely used software)
This commit is contained in:
parent
59dd8d82cf
commit
5e13817563
7 changed files with 167 additions and 93 deletions
|
@ -1,13 +1,22 @@
|
|||
package webshared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"io"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/go-fed/httpsig"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yaronf/httpsign"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/config"
|
||||
"git.mstar.dev/mstar/linstrom/shared"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -22,16 +31,19 @@ Links for home:
|
|||
- https://datatracker.ietf.org/doc/html/rfc9421
|
||||
*/
|
||||
|
||||
func RequestSigned(
|
||||
// Perform a request, signing it as specified in RFC 9421
|
||||
func RequestSignedRFC9421(
|
||||
method, target string,
|
||||
body io.Reader,
|
||||
body []byte,
|
||||
keyId string,
|
||||
privateKeyBytes []byte,
|
||||
useEd bool,
|
||||
) (*http.Response, error) {
|
||||
req, err := http.NewRequest(method, target, body)
|
||||
req, err := http.NewRequest(method, target, bytes.NewBuffer(slices.Clone(body)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyDefaultHeaders(req)
|
||||
var signer *httpsign.Signer
|
||||
signerFields := httpsign.Headers("@request-target", "content-digest")
|
||||
if config.GlobalConfig.Experimental.UseEd25519Keys {
|
||||
|
@ -40,12 +52,18 @@ func RequestSigned(
|
|||
httpsign.NewSignConfig(),
|
||||
signerFields,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
key, err := x509.ParsePKCS1PrivateKey(privateKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer, err = httpsign.NewRSASigner(*key, httpsign.NewSignConfig(), signerFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
client := httpsign.NewClient(
|
||||
RequestClient,
|
||||
|
@ -54,3 +72,80 @@ func RequestSigned(
|
|||
res, err := client.Do(req)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Perform a request, signing it as specified in the cavage proposal
|
||||
// (https://swicg.github.io/activitypub-http-signature/#how-to-sign-a-request)
|
||||
func RequestSignedCavage(
|
||||
method, target string,
|
||||
body []byte,
|
||||
keyId string,
|
||||
privateKeyBytes []byte,
|
||||
useEd bool,
|
||||
) (*http.Response, error) {
|
||||
req, err := http.NewRequest(method, target, bytes.NewBuffer(slices.Clone(body)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyDefaultHeaders(req)
|
||||
var prefs []httpsig.Algorithm
|
||||
var key crypto.PrivateKey
|
||||
if useEd {
|
||||
log.Debug().Msg("Using ed25519 cavage")
|
||||
prefs = append(prefs, httpsig.ED25519)
|
||||
key = ed25519.PrivateKey(privateKeyBytes)
|
||||
} else {
|
||||
log.Debug().Msg("Using RSA cavage")
|
||||
// prefs = append(prefs, httpsig.RSA_SHA512, httpsig.RSA_SHA256)
|
||||
prefs = append(prefs, httpsig.RSA_SHA256)
|
||||
tempKey, err := x509.ParsePKCS1PrivateKey(privateKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = tempKey
|
||||
}
|
||||
digestAlgorithm := httpsig.DigestSha256
|
||||
headersToSign := []string{httpsig.RequestTarget, "date", "host", "user-agent"}
|
||||
if len(body) > 0 {
|
||||
headersToSign = append(headersToSign, "digest")
|
||||
log.Debug().Msg("Non-empty body, adding digest")
|
||||
} else {
|
||||
// Just to ensure the signer doesn't fuck up
|
||||
body = nil
|
||||
}
|
||||
signer, chosenAlgorithm, err := httpsig.NewSigner(
|
||||
prefs,
|
||||
digestAlgorithm,
|
||||
headersToSign,
|
||||
httpsig.Signature,
|
||||
int64(time.Hour),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug().Any("algorithm", chosenAlgorithm).Msg("Signer chose algorithm")
|
||||
if err = signer.SignRequest(key, keyId, req, body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug().Any("headers", req.Header).Msg("Request post signing")
|
||||
return RequestClient.Do(req)
|
||||
}
|
||||
|
||||
func applyDefaultHeaders(r *http.Request) {
|
||||
r.Header.Add(
|
||||
"User-Agent",
|
||||
"Linstrom "+shared.Version+" ("+config.GlobalConfig.General.GetFullDomain()+")",
|
||||
)
|
||||
r.Header.Add("Date", time.Now().UTC().Format(http.TimeFormat))
|
||||
r.Header.Add("Host", config.GlobalConfig.General.GetFullDomain())
|
||||
r.Header.Add("Accept", "application/activity+json")
|
||||
}
|
||||
|
||||
func applyBodyHash(headers http.Header, body []byte) error {
|
||||
if len(body) == 0 {
|
||||
return nil
|
||||
}
|
||||
hash := sha256.Sum256(body)
|
||||
based := base64.StdEncoding.EncodeToString(hash[:])
|
||||
headers.Set("Digest", "SHA-256="+based)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue