125 lines
3.1 KiB
Go
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
|
|
}
|