aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJulio Capote <jcapote@gmail.com>2023-01-05 02:15:57 +0000
committerJulio Capote <jcapote@gmail.com>2023-01-05 02:15:57 +0000
commite9124914cd6b05502cb5dd7f34720b27657560aa (patch)
tree72c68e673a88e8bd88d02ff22c80d0d17ccb8b88 /tools
parent6736b04552c582ab5a07556ba20eca92612daf51 (diff)
downloadcommunique-e9124914cd6b05502cb5dd7f34720b27657560aa.tar.gz
refactor verification into verifier tool, start follow/undo
Diffstat (limited to 'tools')
-rw-r--r--tools/verifier.go81
1 files changed, 81 insertions, 0 deletions
diff --git a/tools/verifier.go b/tools/verifier.go
new file mode 100644
index 0000000..4063ccf
--- /dev/null
+++ b/tools/verifier.go
@@ -0,0 +1,81 @@
+package tools
+
+import (
+ "context"
+ "crypto/x509"
+ "encoding/json"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/go-fed/activity/streams"
+ "github.com/go-fed/activity/streams/vocab"
+ "github.com/go-fed/httpsig"
+ "go.uber.org/zap"
+)
+
+func VerifyRequest(ctx context.Context, req *http.Request, log *zap.SugaredLogger) (vocab.ActivityStreamsPerson, error) {
+ logger := log.With("type", "verifier")
+ verifier, err := httpsig.NewVerifier(req)
+ if err != nil {
+ return nil, err
+ }
+
+ keyId := verifier.KeyId()
+ logger.With("keyId", keyId).Debugf("fetching key page")
+ req, err = http.NewRequest("GET", keyId, nil)
+ req.Header.Set("Accept", "application/json; charset=UTF-8")
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ keyPage, err := io.ReadAll(resp.Body)
+ logger.With("keyId", keyId).With("response", string(keyPage)).Debugf("received response")
+ var keyPageData map[string]interface{}
+ err = json.Unmarshal(keyPage, &keyPageData)
+ if err != nil {
+ return nil, err
+ }
+
+ var person vocab.ActivityStreamsPerson
+
+ resolver, err := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsPerson) error {
+ // Store the person in the enclosing scope, for later.
+ person = p
+ return nil
+ }, func(c context.Context, note vocab.ActivityStreamsNote) error {
+ //TODO not needed, need to figure out how to only pass one func
+ return nil
+ })
+
+ err = resolver.Resolve(ctx, keyPageData)
+ if err != nil {
+ return nil, err
+ }
+
+ pubKeyProp := person.GetW3IDSecurityV1PublicKey()
+ iter := pubKeyProp.At(0) // TODO not safe, use a for instead
+ pubKey := iter.Get()
+ pemProp := pubKey.GetW3IDSecurityV1PublicKeyPem()
+ pemStr := pemProp.Get()
+ logger.With("keyId", keyId).With("pem", pemStr).Debugf("extracted pem")
+ pemObj, _ := pem.Decode([]byte(pemStr))
+ if pemObj == nil {
+ return nil, fmt.Errorf("no PEM block found")
+ }
+ if pemObj.Type != "PUBLIC KEY" {
+ return nil, fmt.Errorf("no public key found in PEM block")
+ }
+
+ decodedKey, err := x509.ParsePKIXPublicKey(pemObj.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ logger.With("keyId", keyId).With("pem", pemStr).Debugf("got %T", decodedKey)
+ algo := httpsig.RSA_SHA256
+ return person, verifier.Verify(decodedKey, algo)
+}