summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-04-02 12:30:10 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-02 11:30:10 +0100
commit4bbdef02f16f82b12d3eaf74b7071833088481df (patch)
treef9d2f51911678cd29c3636ce7d3f69849b7377bc /internal
parent[chore] bump go structr cache version -> v0.6.0 (#2773) (diff)
downloadgotosocial-4bbdef02f16f82b12d3eaf74b7071833088481df.tar.xz
[chore] Try to parse public key as both Actor + bare key (#2710)
* [chore] Try to parse public key as both Actor + bare key * fix weird test fail
Diffstat (limited to 'internal')
-rw-r--r--internal/ap/extract.go108
-rw-r--r--internal/ap/extractpubkey_test.go108
-rw-r--r--internal/federation/authenticate.go50
-rw-r--r--internal/timeline/get_test.go2
-rw-r--r--internal/typeutils/astointernal.go2
5 files changed, 206 insertions, 64 deletions
diff --git a/internal/ap/extract.go b/internal/ap/extract.go
index d9288c162..e0c90c5d7 100644
--- a/internal/ap/extract.go
+++ b/internal/ap/extract.go
@@ -515,9 +515,9 @@ func ExtractURL(i WithURL) (*url.URL, error) {
return nil, gtserror.New("no valid URL property found")
}
-// ExtractPublicKey extracts the public key, public key ID, and public
+// ExtractPubKeyFromActor extracts the public key, public key ID, and public
// key owner ID from an interface, or an error if something goes wrong.
-func ExtractPublicKey(i WithPublicKey) (
+func ExtractPubKeyFromActor(i WithPublicKey) (
*rsa.PublicKey, // pubkey
*url.URL, // pubkey ID
*url.URL, // pubkey owner
@@ -528,6 +528,7 @@ func ExtractPublicKey(i WithPublicKey) (
return nil, nil, nil, gtserror.New("public key property was nil")
}
+ // Take the first public key we can find.
for iter := pubKeyProp.Begin(); iter != pubKeyProp.End(); iter = iter.Next() {
if !iter.IsW3IDSecurityV1PublicKey() {
continue
@@ -538,63 +539,74 @@ func ExtractPublicKey(i WithPublicKey) (
continue
}
- pubKeyID, err := pub.GetId(pkey)
- if err != nil {
- continue
- }
+ return ExtractPubKeyFromKey(pkey)
+ }
- pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner()
- if pubKeyOwnerProp == nil {
- continue
- }
+ return nil, nil, nil, gtserror.New("couldn't find valid public key")
+}
- pubKeyOwner := pubKeyOwnerProp.GetIRI()
- if pubKeyOwner == nil {
- continue
- }
+// ExtractPubKeyFromActor extracts the public key, public key ID, and public
+// key owner ID from an interface, or an error if something goes wrong.
+func ExtractPubKeyFromKey(pkey vocab.W3IDSecurityV1PublicKey) (
+ *rsa.PublicKey, // pubkey
+ *url.URL, // pubkey ID
+ *url.URL, // pubkey owner
+ error,
+) {
+ pubKeyID, err := pub.GetId(pkey)
+ if err != nil {
+ return nil, nil, nil, errors.New("no id set on public key")
+ }
- pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem()
- if pubKeyPemProp == nil {
- continue
- }
+ pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner()
+ if pubKeyOwnerProp == nil {
+ return nil, nil, nil, errors.New("nil pubKeyOwnerProp")
+ }
- pkeyPem := pubKeyPemProp.Get()
- if pkeyPem == "" {
- continue
- }
+ pubKeyOwner := pubKeyOwnerProp.GetIRI()
+ if pubKeyOwner == nil {
+ return nil, nil, nil, errors.New("nil iri on pubKeyOwnerProp")
+ }
- block, _ := pem.Decode([]byte(pkeyPem))
- if block == nil {
- continue
- }
+ pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem()
+ if pubKeyPemProp == nil {
+ return nil, nil, nil, errors.New("nil pubKeyPemProp")
+ }
- var p crypto.PublicKey
- switch block.Type {
- case "PUBLIC KEY":
- p, err = x509.ParsePKIXPublicKey(block.Bytes)
- case "RSA PUBLIC KEY":
- p, err = x509.ParsePKCS1PublicKey(block.Bytes)
- default:
- err = fmt.Errorf("unknown block type: %q", block.Type)
- }
- if err != nil {
- err = gtserror.Newf("could not parse public key from block bytes: %w", err)
- return nil, nil, nil, err
- }
+ pkeyPem := pubKeyPemProp.Get()
+ if pkeyPem == "" {
+ return nil, nil, nil, errors.New("empty pubKeyPemProp")
+ }
- if p == nil {
- return nil, nil, nil, gtserror.New("returned public key was empty")
- }
+ block, _ := pem.Decode([]byte(pkeyPem))
+ if block == nil {
+ return nil, nil, nil, errors.New("nil pubKeyPem")
+ }
- pubKey, ok := p.(*rsa.PublicKey)
- if !ok {
- continue
- }
+ var p crypto.PublicKey
+ switch block.Type {
+ case "PUBLIC KEY":
+ p, err = x509.ParsePKIXPublicKey(block.Bytes)
+ case "RSA PUBLIC KEY":
+ p, err = x509.ParsePKCS1PublicKey(block.Bytes)
+ default:
+ err = fmt.Errorf("unknown block type: %q", block.Type)
+ }
+ if err != nil {
+ err = fmt.Errorf("could not parse public key from block bytes: %w", err)
+ return nil, nil, nil, err
+ }
- return pubKey, pubKeyID, pubKeyOwner, nil
+ if p == nil {
+ return nil, nil, nil, fmt.Errorf("returned public key was empty")
+ }
+
+ pubKey, ok := p.(*rsa.PublicKey)
+ if !ok {
+ return nil, nil, nil, fmt.Errorf("could not type pubKey to *rsa.PublicKey")
}
- return nil, nil, nil, gtserror.New("couldn't find public key")
+ return pubKey, pubKeyID, pubKeyOwner, nil
}
// ExtractContent returns an intermediary representation of
diff --git a/internal/ap/extractpubkey_test.go b/internal/ap/extractpubkey_test.go
new file mode 100644
index 000000000..f7218f8b1
--- /dev/null
+++ b/internal/ap/extractpubkey_test.go
@@ -0,0 +1,108 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package ap_test
+
+import (
+ "context"
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/activity/streams"
+ typepublickey "github.com/superseriousbusiness/activity/streams/impl/w3idsecurityv1/type_publickey"
+ "github.com/superseriousbusiness/gotosocial/internal/ap"
+)
+
+const (
+ stubActor = `{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1"
+ ],
+ "id": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
+ "preferredUsername": "dumpsterqueer",
+ "publicKey": {
+ "id": "https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key",
+ "owner": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7cDz2XfTJXbmmmVXZ3o\nQGB1zu1yP+2/QZZFbLCeM0bMm5cfjJ/olli6kpdcGLh1lFpSgyLE0PlAVNYdSke9\nzcxDao6N16wavFx/bOYhh8HJPPXzlFpNeQQ+EBQ1ivzuLQyzIFTMV4TyZzOREoG9\nizuXuuKDaH/ENDE6qlIDuqtICIjnURjpxnBLldPUxfUvuSO3zY+jTidsxhjUjqkK\nC7RtEVi/D6/CzktVevz5bE/gcAYgKmK0dmkJ9HH6LzOlvkM4Wrq5h/hrM+H1z5e5\nPpdJsl3KlRT4wusuM1Z5xqLQ0oIP4mX/Kd3ypCe150i+jaoCsqBk8OPtl/zKMw1a\nYQIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "type": "Person"
+}`
+
+ key = `{
+ "@context": "https://w3id.org/security/v1",
+ "id": "https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key",
+ "owner": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7cDz2XfTJXbmmmVXZ3o\nQGB1zu1yP+2/QZZFbLCeM0bMm5cfjJ/olli6kpdcGLh1lFpSgyLE0PlAVNYdSke9\nzcxDao6N16wavFx/bOYhh8HJPPXzlFpNeQQ+EBQ1ivzuLQyzIFTMV4TyZzOREoG9\nizuXuuKDaH/ENDE6qlIDuqtICIjnURjpxnBLldPUxfUvuSO3zY+jTidsxhjUjqkK\nC7RtEVi/D6/CzktVevz5bE/gcAYgKmK0dmkJ9HH6LzOlvkM4Wrq5h/hrM+H1z5e5\nPpdJsl3KlRT4wusuM1Z5xqLQ0oIP4mX/Kd3ypCe150i+jaoCsqBk8OPtl/zKMw1a\nYQIDAQAB\n-----END PUBLIC KEY-----\n"
+}`
+)
+
+type ExtractPubKeyTestSuite struct {
+ APTestSuite
+}
+
+func (suite *ExtractPubKeyTestSuite) TestExtractPubKeyFromStub() {
+ m := make(map[string]interface{})
+ if err := json.Unmarshal([]byte(stubActor), &m); err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ t, err := streams.ToType(context.Background(), m)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ wpk, ok := t.(ap.WithPublicKey)
+ if !ok {
+ suite.FailNow("", "could not parse %T as WithPublicKey", t)
+ }
+
+ pubKey, pubKeyID, ownerURI, err := ap.ExtractPubKeyFromActor(wpk)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ suite.NotNil(pubKey)
+ suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key", pubKeyID.String())
+ suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer", ownerURI.String())
+}
+
+func (suite *ExtractPubKeyTestSuite) TestExtractPubKeyFromKey() {
+ m := make(map[string]interface{})
+ if err := json.Unmarshal([]byte(key), &m); err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ pk, err := typepublickey.DeserializePublicKey(m, nil)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ pubKey, pubKeyID, ownerURI, err := ap.ExtractPubKeyFromKey(pk)
+ if err != nil {
+ suite.FailNow(err.Error())
+ }
+
+ suite.NotNil(pubKey)
+ suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key", pubKeyID.String())
+ suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer", ownerURI.String())
+}
+
+func TestExtractPubKeyTestSuite(t *testing.T) {
+ suite.Run(t, &ExtractPubKeyTestSuite{})
+}
diff --git a/internal/federation/authenticate.go b/internal/federation/authenticate.go
index 596233b19..e9263d43c 100644
--- a/internal/federation/authenticate.go
+++ b/internal/federation/authenticate.go
@@ -30,6 +30,7 @@ import (
"codeberg.org/gruf/go-kv"
"github.com/superseriousbusiness/activity/streams"
+ typepublickey "github.com/superseriousbusiness/activity/streams/impl/w3idsecurityv1/type_publickey"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -504,24 +505,45 @@ func parsePubKeyBytes(
return nil, nil, err
}
- t, err := streams.ToType(ctx, m)
- if err != nil {
- return nil, nil, err
- }
+ var (
+ pubKey *rsa.PublicKey
+ ownerURI *url.URL
+ )
- withPublicKey, ok := t.(ap.WithPublicKey)
- if !ok {
- err = gtserror.Newf("resource at %s with type %T could not be converted to ap.WithPublicKey", pubKeyID, t)
- return nil, nil, err
- }
+ if t, err := streams.ToType(ctx, m); err == nil {
+ // See if Actor with a PublicKey attached.
+ wpk, ok := t.(ap.WithPublicKey)
+ if !ok {
+ return nil, nil, gtserror.Newf(
+ "resource at %s with type %T did not contain recognizable public key",
+ pubKeyID, t,
+ )
+ }
- pubKey, _, pubKeyOwnerID, err := ap.ExtractPublicKey(withPublicKey)
- if err != nil {
- err = gtserror.Newf("resource at %s with type %T did not contain recognizable public key", pubKeyID, t)
- return nil, nil, err
+ pubKey, _, ownerURI, err = ap.ExtractPubKeyFromActor(wpk)
+ if err != nil {
+ return nil, nil, gtserror.Newf(
+ "error extracting public key from %T at %s: %w",
+ t, pubKeyID, err,
+ )
+ }
+ } else if pk, err := typepublickey.DeserializePublicKey(m, nil); err == nil {
+ // Bare PublicKey.
+ pubKey, _, ownerURI, err = ap.ExtractPubKeyFromKey(pk)
+ if err != nil {
+ return nil, nil, gtserror.Newf(
+ "error extracting public key at %s: %w",
+ pubKeyID, err,
+ )
+ }
+ } else {
+ return nil, nil, gtserror.Newf(
+ "resource at %s did not contain recognizable public key",
+ pubKeyID,
+ )
}
- return pubKey, pubKeyOwnerID, nil
+ return pubKey, ownerURI, nil
}
var signingAlgorithms = []httpsig.Algorithm{
diff --git a/internal/timeline/get_test.go b/internal/timeline/get_test.go
index 02256054e..499a9f35d 100644
--- a/internal/timeline/get_test.go
+++ b/internal/timeline/get_test.go
@@ -688,7 +688,7 @@ func (suite *GetTestSuite) TestGetTimelinesAsync() {
limit,
local,
); err != nil {
- suite.FailNow(err.Error())
+ suite.Fail(err.Error())
}
wg.Done()
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go
index b5e713554..ba370790a 100644
--- a/internal/typeutils/astointernal.go
+++ b/internal/typeutils/astointernal.go
@@ -211,7 +211,7 @@ func (c *Converter) ASRepresentationToAccount(ctx context.Context, accountable a
}
// Extract account public key and verify ownership to account.
- pkey, pkeyURL, pkeyOwnerID, err := ap.ExtractPublicKey(accountable)
+ pkey, pkeyURL, pkeyOwnerID, err := ap.ExtractPubKeyFromActor(accountable)
if err != nil {
err := gtserror.Newf("error extracting public key for %s: %w", uri, err)
return nil, gtserror.SetMalformed(err)