linstrom/web/shared/clientRfc9421.go
mstar 08f6de0bd7
All checks were successful
/ docker (push) Successful in 4m1s
Rename cavage singing func, add import for server
2025-04-15 14:51:07 +02:00

125 lines
3.1 KiB
Go

package webshared
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"net/http"
"slices"
"time"
"github.com/yaronf/httpsign"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/shared"
"git.mstar.dev/mstar/linstrom/storage-new/models"
)
/*
Links for home:
- https://pkg.go.dev/github.com/yaronf/httpsign#Client.Do
- https://www.ietf.org/archive/id/draft-richanna-http-message-signatures-00.html
- https://github.com/mastodon/mastodon/issues/29905
- https://github.com/fedify-dev/fedify/issues/208
- https://github.com/mastodon/mastodon/issues/21429
- https://github.com/go-ap/fedbox/blob/master/httpsig.go
- https://swicg.github.io/activitypub-http-signature/
- https://datatracker.ietf.org/doc/html/rfc9421
*/
// Perform a request, signing it as specified in RFC 9421
func RequestSignedRFC9421(
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 signer *httpsign.Signer
signerFields := httpsign.Headers("@request-target", "content-digest")
if config.GlobalConfig.Experimental.UseEd25519Keys {
signer, err = httpsign.NewEd25519Signer(
privateKeyBytes,
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,
httpsign.NewClientConfig().SetSigner(signer).SetSignatureName("sig1"),
)
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,
actor *models.User,
) (*http.Response, error) {
req, err := NewRequest(method, target, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/activity+json")
var keyBytes []byte
if config.GlobalConfig.Experimental.UseEd25519Keys {
keyBytes = actor.PrivateKeyEd
} else {
keyBytes = actor.PrivateKeyRsa
}
// Sign and send
err = SignRequest(
req,
actor.ID+"#main-key",
keyBytes,
body,
)
// err = webshared.SignRequestWithHttpsig(req, linstromActor.ID+"#main-key", keyBytes, nil)
if err != nil {
return nil, err
}
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
}