IT FUCKING WORKS
All checks were successful
/ docker (push) Successful in 4m9s

(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:
Melody Becker 2025-04-14 23:14:30 +02:00
parent 59dd8d82cf
commit 5e13817563
Signed by: mstar
SSH key fingerprint: SHA256:vkXfS9FG2pVNVfvDrzd1VW9n8VJzqqdKQGljxxX8uK8
7 changed files with 167 additions and 93 deletions

View file

@ -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
}