mirror of
https://github.com/mastodon/mastodon.git
synced 2026-08-25 23:04:47 -05:00
Add support for importing Ed25519 public keys
This commit is contained in:
@@ -22,7 +22,7 @@ class ActivityPub::LinkedDataSignature
|
||||
|
||||
keypair = Keypair.from_keyid(creator_uri)
|
||||
keypair = ActivityPub::FetchRemoteKeyService.new.call(creator_uri) if keypair&.public_key.blank?
|
||||
return if keypair.nil? || !keypair.usable?
|
||||
return if keypair.nil? || !keypair.usable? || keypair.type != 'rsa'
|
||||
|
||||
options_hash = hash(@json['signature'].without('type', 'id', 'signatureValue').merge('@context' => CONTEXT))
|
||||
document_hash = hash(@json.without('signature'))
|
||||
|
||||
@@ -25,7 +25,10 @@ class Keypair < ApplicationRecord
|
||||
|
||||
belongs_to :account
|
||||
|
||||
enum :type, { rsa: 0 }
|
||||
enum :type, {
|
||||
rsa: 0,
|
||||
ed25519: 1,
|
||||
}, validate: true
|
||||
|
||||
attr_accessor :require_private_key
|
||||
|
||||
@@ -46,6 +49,8 @@ class Keypair < ApplicationRecord
|
||||
case type
|
||||
when 'rsa'
|
||||
OpenSSL::PKey::RSA.new(private_key || public_key)
|
||||
when 'ed25519'
|
||||
OpenSSL::PKey.read(private_key || public_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -341,6 +341,14 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
case tag
|
||||
when :'rsa-pub'
|
||||
[:rsa, OpenSSL::PKey::RSA.new(key).to_pem]
|
||||
when :'ed25519-pub'
|
||||
asn1 = OpenSSL::ASN1::Sequence(
|
||||
[
|
||||
OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId('ED25519')]),
|
||||
OpenSSL::ASN1::BitString(key),
|
||||
]
|
||||
)
|
||||
[:ed25519, OpenSSL::PKey.read(asn1.to_der).public_to_pem]
|
||||
end
|
||||
rescue ArgumentError
|
||||
nil
|
||||
|
||||
@@ -110,7 +110,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService do
|
||||
stub_request(:get, public_key_id).to_return(body: key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] }).to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the nil' do
|
||||
it 'returns nil' do
|
||||
expect(keypair).to be_nil
|
||||
end
|
||||
end
|
||||
@@ -127,6 +127,13 @@ RSpec.describe ActivityPub::FetchRemoteKeyService do
|
||||
publicKeyMultibase: 'z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK',
|
||||
}
|
||||
end
|
||||
let(:ed25519_key_pem) do
|
||||
<<~TEXT
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEALm/M42cB3HkUiODQsXRcweM6TByfzEHGO9ND274JcOY=
|
||||
-----END PUBLIC KEY-----
|
||||
TEXT
|
||||
end
|
||||
|
||||
let(:rsa_key_id) { 'https://example.com/alice#rsa-key' }
|
||||
let(:actor_rsa_key) { rsa_multikey }
|
||||
@@ -218,10 +225,63 @@ RSpec.describe ActivityPub::FetchRemoteKeyService do
|
||||
stub_request(:get, rsa_key_id).to_return(body: rsa_multikey.merge({ '@context': ['https://www.w3.org/ns/cid/v1'] }).to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the nil' do
|
||||
it 'returns nil' do
|
||||
expect(keypair).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an Ed25519 key' do
|
||||
let(:keypair) { subject.call(ed25519_key_id) }
|
||||
|
||||
context 'when the key is a sub-object from the actor' do
|
||||
before do
|
||||
stub_request(:get, ed25519_key_id).to_return(body: actor.to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the expected account' do
|
||||
expect(keypair.account.uri).to eq 'https://example.com/alice'
|
||||
|
||||
expect(keypair)
|
||||
.to have_attributes(
|
||||
uri: ed25519_key_id,
|
||||
type: 'ed25519',
|
||||
public_key: ed25519_key_pem
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the key is a separate document' do
|
||||
let(:ed25519_key_id) { 'https://example.com/alice-public-key.json' }
|
||||
let(:actor_ed25519_key) { ed25519_key_id }
|
||||
|
||||
before do
|
||||
stub_request(:get, ed25519_key_id).to_return(body: ed25519_multikey.merge({ '@context': ['https://www.w3.org/ns/cid/v1'] }).to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the expected account' do
|
||||
expect(keypair.account.uri).to eq 'https://example.com/alice'
|
||||
expect(keypair)
|
||||
.to have_attributes(
|
||||
uri: ed25519_key_id,
|
||||
type: 'ed25519',
|
||||
public_key: ed25519_key_pem
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the key and owner do not match' do
|
||||
let(:ed25519_key_id) { 'https://example.com/fake-public-key.json' }
|
||||
let(:actor_ed25519_key) { 'https://example.com/alice-public-key.json' }
|
||||
|
||||
before do
|
||||
stub_request(:get, ed25519_key_id).to_return(body: ed25519_multikey.merge({ '@context': ['https://www.w3.org/ns/cid/v1'] }).to_json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(keypair).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user