linked_data_signature.rb 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. # frozen_string_literal: true
  2. class ActivityPub::LinkedDataSignature
  3. include JsonLdHelper
  4. CONTEXT = 'https://w3id.org/identity/v1'
  5. def initialize(json)
  6. @json = json.with_indifferent_access
  7. end
  8. def verify_account!
  9. return unless @json['signature'].is_a?(Hash)
  10. type = @json['signature']['type']
  11. creator_uri = @json['signature']['creator']
  12. signature = @json['signature']['signatureValue']
  13. return unless type == 'RsaSignature2017'
  14. creator = ActivityPub::TagManager.instance.uri_to_resource(creator_uri, Account)
  15. creator ||= ActivityPub::FetchRemoteKeyService.new.call(creator_uri)
  16. return if creator.nil?
  17. options_hash = hash(@json['signature'].without('type', 'id', 'signatureValue').merge('@context' => CONTEXT))
  18. document_hash = hash(@json.without('signature'))
  19. to_be_verified = options_hash + document_hash
  20. if creator.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), to_be_verified)
  21. creator
  22. end
  23. end
  24. def sign!(creator)
  25. options = {
  26. 'type' => 'RsaSignature2017',
  27. 'creator' => [ActivityPub::TagManager.instance.uri_for(creator), '#main-key'].join,
  28. 'created' => Time.now.utc.iso8601,
  29. }
  30. options_hash = hash(options.without('type', 'id', 'signatureValue').merge('@context' => CONTEXT))
  31. document_hash = hash(@json.without('signature'))
  32. to_be_signed = options_hash + document_hash
  33. signature = Base64.strict_encode64(creator.keypair.sign(OpenSSL::Digest::SHA256.new, to_be_signed))
  34. @json.merge('signature' => options.merge('signatureValue' => signature))
  35. end
  36. private
  37. def hash(obj)
  38. Digest::SHA256.hexdigest(canonicalize(obj))
  39. end
  40. end