1 # frozen_string_literal: true
3 class ActivityPub
::LinkedDataSignature
6 CONTEXT
= 'https://w3id.org/identity/v1'
9 @json = json
.with_indifferent_access
13 return unless @json['signature'].is_a
?(Hash
)
15 type
= @json['signature']['type']
16 creator_uri
= @json['signature']['creator']
17 signature
= @json['signature']['signatureValue']
19 return unless type
== 'RsaSignature2017'
21 creator
= ActivityPub
::TagManager.instance
.uri_to_actor(creator_uri
)
22 # 4.1.12 uses a reassignment `if creator&.public_key.blank?`
23 creator
||= ActivityPub
::FetchRemoteKeyService.new
.call(creator_uri
)
25 return if creator
.nil?
27 options_hash
= hash(@json['signature'].without('type', 'id', 'signatureValue').merge('@context' => CONTEXT
))
28 document_hash
= hash(@json.without('signature'))
29 to_be_verified
= options_hash + document_hash
31 if creator
.keypair
.public_key
.verify(OpenSSL
::Digest.new('SHA256'), Base64
.decode64(signature
), to_be_verified
)
36 def sign!
(creator
, sign_with
: nil)
38 'type' => 'RsaSignature2017',
39 'creator' => ActivityPub
::TagManager.instance
.key_uri_for(creator
),
40 'created' => Time
.now
.utc
.iso8601
,
43 options_hash
= hash(options
.without('type', 'id', 'signatureValue').merge('@context' => CONTEXT
))
44 document_hash
= hash(@json.without('signature'))
45 to_be_signed
= options_hash + document_hash
46 keypair
= sign_with
.present
? ? OpenSSL
::PKey::RSA.new(sign_with
) : creator
.keypair
48 signature
= Base64
.strict_encode64(keypair
.sign(OpenSSL
::Digest.new('SHA256'), to_be_signed
))
50 @json.merge('signature' => options
.merge('signatureValue' => signature
))
56 Digest
::SHA256.hexdigest(canonicalize(obj
))