diff options
author | 2024-04-02 12:30:10 +0200 | |
---|---|---|
committer | 2024-04-02 11:30:10 +0100 | |
commit | 4bbdef02f16f82b12d3eaf74b7071833088481df (patch) | |
tree | f9d2f51911678cd29c3636ce7d3f69849b7377bc /internal/ap | |
parent | [chore] bump go structr cache version -> v0.6.0 (#2773) (diff) | |
download | gotosocial-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/ap')
-rw-r--r-- | internal/ap/extract.go | 108 | ||||
-rw-r--r-- | internal/ap/extractpubkey_test.go | 108 |
2 files changed, 168 insertions, 48 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{}) +} |