Remove unused E2EE messaging code (#31193)

This commit is contained in:
Matt Jankowski 2024-09-18 05:27:43 -04:00 committed by GitHub
parent 2d399f5d4a
commit 5405bdd344
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
130 changed files with 25 additions and 1347 deletions

View File

@ -47,7 +47,6 @@ gem 'color_diff', '~> 0.1'
gem 'csv', '~> 3.2' gem 'csv', '~> 3.2'
gem 'discard', '~> 1.2' gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.6' gem 'doorkeeper', '~> 5.6'
gem 'ed25519', '~> 1.3'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
gem 'hiredis', '~> 0.6' gem 'hiredis', '~> 0.6'

View File

@ -214,7 +214,6 @@ GEM
railties (>= 5) railties (>= 5)
dotenv (3.1.4) dotenv (3.1.4)
drb (2.2.1) drb (2.2.1)
ed25519 (1.3.0)
elasticsearch (7.17.11) elasticsearch (7.17.11)
elasticsearch-api (= 7.17.11) elasticsearch-api (= 7.17.11)
elasticsearch-transport (= 7.17.11) elasticsearch-transport (= 7.17.11)
@ -937,7 +936,6 @@ DEPENDENCIES
discard (~> 1.2) discard (~> 1.2)
doorkeeper (~> 5.6) doorkeeper (~> 5.6)
dotenv dotenv
ed25519 (~> 1.3)
email_spec email_spec
fabrication (~> 2.30) fabrication (~> 2.30)
faker (~> 3.2) faker (~> 3.2)

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class ActivityPub::ClaimsController < ActivityPub::BaseController
skip_before_action :authenticate_user!
before_action :require_account_signature!
before_action :set_claim_result
def create
render json: @claim_result, serializer: ActivityPub::OneTimeKeySerializer
end
private
def set_claim_result
@claim_result = ::Keys::ClaimService.new.call(@account.id, params[:id])
end
end

View File

@ -22,8 +22,6 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) } @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
when 'tags' when 'tags'
@items = for_signed_account { @account.featured_tags } @items = for_signed_account { @account.featured_tags }
when 'devices'
@items = @account.devices
else else
not_found not_found
end end
@ -31,7 +29,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_size def set_size
case params[:id] case params[:id]
when 'featured', 'devices', 'tags' when 'featured', 'tags'
@size = @items.size @size = @items.size
else else
not_found not_found
@ -42,7 +40,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
case params[:id] case params[:id]
when 'featured' when 'featured'
@type = :ordered @type = :ordered
when 'devices', 'tags' when 'tags'
@type = :unordered @type = :unordered
else else
not_found not_found

View File

@ -1,30 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::DeliveriesController < Api::BaseController
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
before_action :set_current_device
def create
devices.each do |device_params|
DeliverToDeviceService.new.call(current_account, @current_device, device_params)
end
render_empty
end
private
def set_current_device
@current_device = Device.find_by!(access_token: doorkeeper_token)
end
def resource_params
params.require(:device)
params.permit(device: [:account_id, :device_id, :type, :body, :hmac])
end
def devices
Array(resource_params[:device])
end
end

View File

@ -1,47 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
LIMIT = 80
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
before_action :set_current_device
before_action :set_encrypted_messages, only: :index
after_action :insert_pagination_headers, only: :index
def index
render json: @encrypted_messages, each_serializer: REST::EncryptedMessageSerializer
end
def clear
@current_device.encrypted_messages.up_to(params[:up_to_id]).delete_all
render_empty
end
private
def set_current_device
@current_device = Device.find_by!(access_token: doorkeeper_token)
end
def set_encrypted_messages
@encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def next_path
api_v1_crypto_encrypted_messages_url pagination_params(max_id: pagination_max_id) if records_continue?
end
def prev_path
api_v1_crypto_encrypted_messages_url pagination_params(min_id: pagination_since_id) unless @encrypted_messages.empty?
end
def pagination_collection
@encrypted_messages
end
def records_continue?
@encrypted_messages.size == limit_param(LIMIT)
end
end

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::Keys::ClaimsController < Api::BaseController
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
before_action :set_claim_results
def create
render json: @claim_results, each_serializer: REST::Keys::ClaimResultSerializer
end
private
def set_claim_results
@claim_results = devices.filter_map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) }
end
def resource_params
params.permit(device: [:account_id, :device_id])
end
def devices
Array(resource_params[:device])
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::Keys::CountsController < Api::BaseController
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
before_action :set_current_device
def show
render json: { one_time_keys: @current_device.one_time_keys.count }
end
private
def set_current_device
@current_device = Device.find_by!(access_token: doorkeeper_token)
end
end

View File

@ -1,26 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::Keys::QueriesController < Api::BaseController
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
before_action :set_accounts
before_action :set_query_results
def create
render json: @query_results, each_serializer: REST::Keys::QueryResultSerializer
end
private
def set_accounts
@accounts = Account.where(id: account_ids).includes(:devices)
end
def set_query_results
@query_results = @accounts.filter_map { |account| ::Keys::QueryService.new.call(account) }
end
def account_ids
Array(params[:id]).map(&:to_i)
end
end

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
class Api::V1::Crypto::Keys::UploadsController < Api::BaseController
before_action -> { doorkeeper_authorize! :crypto }
before_action :require_user!
def create
device = Device.find_or_initialize_by(access_token: doorkeeper_token)
device.transaction do
device.account = current_account
device.update!(resource_params[:device])
if resource_params[:one_time_keys].present? && resource_params[:one_time_keys].is_a?(Enumerable)
resource_params[:one_time_keys].each do |one_time_key_params|
device.one_time_keys.create!(one_time_key_params)
end
end
end
render json: device, serializer: REST::Keys::DeviceSerializer
end
private
def resource_params
params.permit(device: [:device_id, :name, :fingerprint_key, :identity_key], one_time_keys: [:key_id, :key, :signature])
end
end

View File

@ -23,23 +23,6 @@ module ContextHelper
indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' }, indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' },
memorial: { 'toot' => 'http://joinmastodon.org/ns#', 'memorial' => 'toot:memorial' }, memorial: { 'toot' => 'http://joinmastodon.org/ns#', 'memorial' => 'toot:memorial' },
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
olm: {
'toot' => 'http://joinmastodon.org/ns#',
'Device' => 'toot:Device',
'Ed25519Signature' => 'toot:Ed25519Signature',
'Ed25519Key' => 'toot:Ed25519Key',
'Curve25519Key' => 'toot:Curve25519Key',
'EncryptedMessage' => 'toot:EncryptedMessage',
'publicKeyBase64' => 'toot:publicKeyBase64',
'deviceId' => 'toot:deviceId',
'claim' => { '@type' => '@id', '@id' => 'toot:claim' },
'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' },
'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' },
'devices' => { '@type' => '@id', '@id' => 'toot:devices' },
'messageFranking' => 'toot:messageFranking',
'messageType' => 'toot:messageType',
'cipherText' => 'toot:cipherText',
},
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } }, attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } },
}.freeze }.freeze

View File

@ -8,44 +8,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
dereference_object! dereference_object!
case @object['type']
when 'EncryptedMessage'
create_encrypted_message
else
create_status create_status
end end
end
private private
def create_encrypted_message
return reject_payload! if non_matching_uri_hosts?(@account.uri, object_uri) || @options[:delivered_to_account_id].blank?
target_account = Account.find(@options[:delivered_to_account_id])
target_device = target_account.devices.find_by(device_id: @object.dig('to', 'deviceId'))
return if target_device.nil?
target_device.encrypted_messages.create!(
from_account: @account,
from_device_id: @object.dig('attributedTo', 'deviceId'),
type: @object['messageType'],
body: @object['cipherText'],
digest: @object.dig('digest', 'digestValue'),
message_franking: message_franking.to_token
)
end
def message_franking
MessageFranking.new(
hmac: @object.dig('digest', 'digestValue'),
original_franking: @object['messageFranking'],
source_account_id: @account.id,
target_account_id: @options[:delivered_to_account_id],
timestamp: Time.now.utc
)
end
def create_status def create_status
return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity? return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity?

View File

@ -20,8 +20,6 @@ class InlineRenderer
serializer = REST::AnnouncementSerializer serializer = REST::AnnouncementSerializer
when :reaction when :reaction
serializer = REST::ReactionSerializer serializer = REST::ReactionSerializer
when :encrypted_message
serializer = REST::EncryptedMessageSerializer
else else
return return
end end

View File

@ -1,13 +0,0 @@
# frozen_string_literal: true
class Vacuum::SystemKeysVacuum
def perform
vacuum_expired_system_keys!
end
private
def vacuum_expired_system_keys!
SystemKey.expired.delete_all
end
end

View File

@ -44,7 +44,6 @@
# hide_collections :boolean # hide_collections :boolean
# avatar_storage_schema_version :integer # avatar_storage_schema_version :integer
# header_storage_schema_version :integer # header_storage_schema_version :integer
# devices_url :string
# suspension_origin :integer # suspension_origin :integer
# sensitized_at :datetime # sensitized_at :datetime
# trendable :boolean # trendable :boolean
@ -56,11 +55,12 @@
class Account < ApplicationRecord class Account < ApplicationRecord
self.ignored_columns += %w( self.ignored_columns += %w(
subscription_expires_at devices_url
secret hub_url
remote_url remote_url
salmon_url salmon_url
hub_url secret
subscription_expires_at
trust_level trust_level
) )

View File

@ -7,9 +7,6 @@ module Account::Associations
# Local users # Local users
has_one :user, inverse_of: :account, dependent: :destroy has_one :user, inverse_of: :account, dependent: :destroy
# E2EE
has_many :devices, dependent: :destroy, inverse_of: :account
# Timelines # Timelines
has_many :statuses, inverse_of: :account, dependent: :destroy has_many :statuses, inverse_of: :account, dependent: :destroy
has_many :favourites, inverse_of: :account, dependent: :destroy has_many :favourites, inverse_of: :account, dependent: :destroy

View File

@ -1,36 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: devices
#
# id :bigint(8) not null, primary key
# access_token_id :bigint(8)
# account_id :bigint(8)
# device_id :string default(""), not null
# name :string default(""), not null
# fingerprint_key :text default(""), not null
# identity_key :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Device < ApplicationRecord
belongs_to :access_token, class_name: 'Doorkeeper::AccessToken'
belongs_to :account
has_many :one_time_keys, dependent: :destroy, inverse_of: :device
has_many :encrypted_messages, dependent: :destroy, inverse_of: :device
validates :name, :fingerprint_key, :identity_key, presence: true
validates :fingerprint_key, :identity_key, ed25519_key: true
before_save :invalidate_associations, if: -> { device_id_changed? || fingerprint_key_changed? || identity_key_changed? }
private
def invalidate_associations
one_time_keys.destroy_all
encrypted_messages.destroy_all
end
end

View File

@ -1,49 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: encrypted_messages
#
# id :bigint(8) not null, primary key
# device_id :bigint(8)
# from_account_id :bigint(8)
# from_device_id :string default(""), not null
# type :integer default(0), not null
# body :text default(""), not null
# digest :text default(""), not null
# message_franking :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class EncryptedMessage < ApplicationRecord
self.inheritance_column = nil
include Paginable
include Redisable
scope :up_to, ->(id) { where(arel_table[:id].lteq(id)) }
belongs_to :device
belongs_to :from_account, class_name: 'Account'
around_create Mastodon::Snowflake::Callbacks
after_commit :push_to_streaming_api
private
def push_to_streaming_api
return if destroyed? || !subscribed_to_timeline?
PushEncryptedMessageWorker.perform_async(id)
end
def subscribed_to_timeline?
redis.exists?("subscribed:#{streaming_channel}")
end
def streaming_channel
"timeline:#{device.account_id}:#{device.device_id}"
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
class MessageFranking
attr_reader :hmac, :source_account_id, :target_account_id,
:timestamp, :original_franking
def initialize(attributes = {})
@hmac = attributes[:hmac]
@source_account_id = attributes[:source_account_id]
@target_account_id = attributes[:target_account_id]
@timestamp = attributes[:timestamp]
@original_franking = attributes[:original_franking]
end
def to_token
crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj)
crypt.encrypt_and_sign(self)
end
end

View File

@ -1,22 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: one_time_keys
#
# id :bigint(8) not null, primary key
# device_id :bigint(8)
# key_id :string default(""), not null
# key :text default(""), not null
# signature :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class OneTimeKey < ApplicationRecord
belongs_to :device
validates :key_id, :key, :signature, presence: true
validates :key, ed25519_key: true
validates :signature, ed25519_signature: { message: :key, verify_key: ->(one_time_key) { one_time_key.device.fingerprint_key } }
end

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: system_keys
#
# id :bigint(8) not null, primary key
# key :binary
# created_at :datetime not null
# updated_at :datetime not null
#
class SystemKey < ApplicationRecord
ROTATION_PERIOD = 1.week.freeze
before_validation :set_key
scope :expired, ->(now = Time.now.utc) { where(arel_table[:created_at].lt(now - (ROTATION_PERIOD * 3))) }
class << self
def current_key
previous_key = order(id: :asc).last
if previous_key && previous_key.created_at >= ROTATION_PERIOD.ago
previous_key.key
else
create.key
end
end
end
private
def set_key
return if key.present?
cipher = OpenSSL::Cipher.new('AES-256-GCM')
cipher.encrypt
self.key = cipher.random_key
end
end

View File

@ -5,8 +5,6 @@ class ActivityPub::ActivitySerializer < ActivityPub::Serializer
case model.class.name case model.class.name
when 'Status' when 'Status'
ActivityPub::NoteSerializer ActivityPub::NoteSerializer
when 'DeliverToDeviceService::EncryptedMessage'
ActivityPub::EncryptedMessageSerializer
else else
super super
end end

View File

@ -7,7 +7,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
context :security context :security
context_extensions :manually_approves_followers, :featured, :also_known_as, context_extensions :manually_approves_followers, :featured, :also_known_as,
:moved_to, :property_value, :discoverable, :olm, :suspended, :moved_to, :property_value, :discoverable, :suspended,
:memorial, :indexable, :attribution_domains :memorial, :indexable, :attribution_domains
attributes :id, :type, :following, :followers, attributes :id, :type, :following, :followers,
@ -21,7 +21,6 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
has_many :virtual_tags, key: :tag has_many :virtual_tags, key: :tag
has_many :virtual_attachments, key: :attachment has_many :virtual_attachments, key: :attachment
attribute :devices, unless: :instance_actor?
attribute :moved_to, if: :moved? attribute :moved_to, if: :moved?
attribute :also_known_as, if: :also_known_as? attribute :also_known_as, if: :also_known_as?
attribute :suspended, if: :suspended? attribute :suspended, if: :suspended?
@ -72,10 +71,6 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object) object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object)
end end
def devices
account_collection_url(object, :devices)
end
def outbox def outbox
object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object) object.instance_actor? ? instance_actor_outbox_url : account_outbox_url(object)
end end

View File

@ -14,8 +14,6 @@ class ActivityPub::CollectionSerializer < ActivityPub::Serializer
case model.class.name case model.class.name
when 'Status' when 'Status'
ActivityPub::NoteSerializer ActivityPub::NoteSerializer
when 'Device'
ActivityPub::DeviceSerializer
when 'FeaturedTag' when 'FeaturedTag'
ActivityPub::HashtagSerializer ActivityPub::HashtagSerializer
when 'ActivityPub::CollectionPresenter' when 'ActivityPub::CollectionPresenter'

View File

@ -1,52 +0,0 @@
# frozen_string_literal: true
class ActivityPub::DeviceSerializer < ActivityPub::Serializer
context_extensions :olm
include RoutingHelper
class FingerprintKeySerializer < ActivityPub::Serializer
attributes :type, :public_key_base64
def type
'Ed25519Key'
end
def public_key_base64
object.fingerprint_key
end
end
class IdentityKeySerializer < ActivityPub::Serializer
attributes :type, :public_key_base64
def type
'Curve25519Key'
end
def public_key_base64
object.identity_key
end
end
attributes :device_id, :type, :name, :claim
has_one :fingerprint_key, serializer: FingerprintKeySerializer
has_one :identity_key, serializer: IdentityKeySerializer
def type
'Device'
end
def claim
account_claim_url(object.account, id: object.device_id)
end
def fingerprint_key
object
end
def identity_key
object
end
end

View File

@ -1,61 +0,0 @@
# frozen_string_literal: true
class ActivityPub::EncryptedMessageSerializer < ActivityPub::Serializer
context :security
context_extensions :olm
class DeviceSerializer < ActivityPub::Serializer
attributes :type, :device_id
def type
'Device'
end
def device_id
object
end
end
class DigestSerializer < ActivityPub::Serializer
attributes :type, :digest_algorithm, :digest_value
def type
'Digest'
end
def digest_algorithm
'http://www.w3.org/2000/09/xmldsig#hmac-sha256'
end
def digest_value
object
end
end
attributes :type, :message_type, :cipher_text, :message_franking
has_one :attributed_to, serializer: DeviceSerializer
has_one :to, serializer: DeviceSerializer
has_one :digest, serializer: DigestSerializer
def type
'EncryptedMessage'
end
def attributed_to
object.source_device.device_id
end
def to
object.target_device_id
end
def message_type
object.type
end
def cipher_text
object.body
end
end

View File

@ -1,35 +0,0 @@
# frozen_string_literal: true
class ActivityPub::OneTimeKeySerializer < ActivityPub::Serializer
context :security
context_extensions :olm
class SignatureSerializer < ActivityPub::Serializer
attributes :type, :signature_value
def type
'Ed25519Signature'
end
def signature_value
object.signature
end
end
attributes :key_id, :type, :public_key_base64
has_one :signature, serializer: SignatureSerializer
def type
'Curve25519Key'
end
def public_key_base64
object.key
end
def signature
object
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
class REST::EncryptedMessageSerializer < ActiveModel::Serializer
attributes :id, :account_id, :device_id,
:type, :body, :digest, :message_franking,
:created_at
def id
object.id.to_s
end
def account_id
object.from_account_id.to_s
end
def device_id
object.from_device_id
end
end

View File

@ -1,9 +0,0 @@
# frozen_string_literal: true
class REST::Keys::ClaimResultSerializer < ActiveModel::Serializer
attributes :account_id, :device_id, :key_id, :key, :signature
def account_id
object.account.id.to_s
end
end

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
class REST::Keys::DeviceSerializer < ActiveModel::Serializer
attributes :device_id, :name, :identity_key,
:fingerprint_key
end

View File

@ -1,11 +0,0 @@
# frozen_string_literal: true
class REST::Keys::QueryResultSerializer < ActiveModel::Serializer
attributes :account_id
has_many :devices, serializer: REST::Keys::DeviceSerializer
def account_id
object.account.id.to_s
end
end

View File

@ -108,7 +108,6 @@ class ActivityPub::ProcessAccountService < BaseService
def set_immediate_attributes! def set_immediate_attributes!
@account.featured_collection_url = @json['featured'] || '' @account.featured_collection_url = @json['featured'] || ''
@account.devices_url = @json['devices'] || ''
@account.display_name = @json['name'] || '' @account.display_name = @json['name'] || ''
@account.note = @json['summary'] || '' @account.note = @json['summary'] || ''
@account.locked = @json['manuallyApprovesFollowers'] || false @account.locked = @json['manuallyApprovesFollowers'] || false

View File

@ -13,7 +13,6 @@ class DeleteAccountService < BaseService
conversation_mutes conversation_mutes
conversations conversations
custom_filters custom_filters
devices
domain_blocks domain_blocks
featured_tags featured_tags
follow_requests follow_requests
@ -40,7 +39,6 @@ class DeleteAccountService < BaseService
conversation_mutes conversation_mutes
conversations conversations
custom_filters custom_filters
devices
domain_blocks domain_blocks
featured_tags featured_tags
follow_requests follow_requests

View File

@ -1,78 +0,0 @@
# frozen_string_literal: true
class DeliverToDeviceService < BaseService
include Payloadable
class EncryptedMessage < ActiveModelSerializers::Model
attributes :source_account, :target_account, :source_device,
:target_device_id, :type, :body, :digest,
:message_franking
end
def call(source_account, source_device, options = {})
@source_account = source_account
@source_device = source_device
@target_account = Account.find(options[:account_id])
@target_device_id = options[:device_id]
@body = options[:body]
@type = options[:type]
@hmac = options[:hmac]
set_message_franking!
if @target_account.local?
deliver_to_local!
else
deliver_to_remote!
end
end
private
def set_message_franking!
@message_franking = message_franking.to_token
end
def deliver_to_local!
target_device = @target_account.devices.find_by!(device_id: @target_device_id)
target_device.encrypted_messages.create!(
from_account: @source_account,
from_device_id: @source_device.device_id,
type: @type,
body: @body,
digest: @hmac,
message_franking: @message_franking
)
end
def deliver_to_remote!
ActivityPub::DeliveryWorker.perform_async(
Oj.dump(serialize_payload(ActivityPub::ActivityPresenter.from_encrypted_message(encrypted_message), ActivityPub::ActivitySerializer)),
@source_account.id,
@target_account.inbox_url
)
end
def message_franking
MessageFranking.new(
source_account_id: @source_account.id,
target_account_id: @target_account.id,
hmac: @hmac,
timestamp: Time.now.utc
)
end
def encrypted_message
EncryptedMessage.new(
source_account: @source_account,
target_account: @target_account,
source_device: @source_device,
target_device_id: @target_device_id,
type: @type,
body: @body,
digest: @hmac,
message_franking: @message_franking
)
end
end

View File

@ -1,79 +0,0 @@
# frozen_string_literal: true
class Keys::ClaimService < BaseService
HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
class Result < ActiveModelSerializers::Model
attributes :account, :device_id, :key_id,
:key, :signature
def initialize(account, device_id, key_attributes = {})
super(
account: account,
device_id: device_id,
key_id: key_attributes[:key_id],
key: key_attributes[:key],
signature: key_attributes[:signature],
)
end
end
def call(source_account, target_account_id, device_id)
@source_account = source_account
@target_account = Account.find(target_account_id)
@device_id = device_id
if @target_account.local?
claim_local_key!
else
claim_remote_key!
end
rescue ActiveRecord::RecordNotFound
nil
end
private
def claim_local_key!
device = @target_account.devices.find_by(device_id: @device_id)
key = nil
ApplicationRecord.transaction do
key = device.one_time_keys.order(Arel.sql('random()')).first!
key.destroy!
end
@result = Result.new(@target_account, @device_id, key)
end
def claim_remote_key!
query_result = QueryService.new.call(@target_account)
device = query_result.find(@device_id)
return unless device.present? && device.valid_claim_url?
json = fetch_resource_with_post(device.claim_url)
return unless json.present? && json['publicKeyBase64'].present?
@result = Result.new(@target_account, @device_id, key_id: json['id'], key: json['publicKeyBase64'], signature: json.dig('signature', 'signatureValue'))
rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e
Rails.logger.debug { "Claiming one-time key for #{@target_account.acct}:#{@device_id} failed: #{e}" }
nil
end
def fetch_resource_with_post(uri)
build_post_request(uri).perform do |response|
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response)
body_to_json(response.body_with_limit) if response.code == 200
end
end
def build_post_request(uri)
Request.new(:post, uri).tap do |request|
request.on_behalf_of(@source_account)
request.add_headers(HEADERS)
end
end
end

View File

@ -1,79 +0,0 @@
# frozen_string_literal: true
class Keys::QueryService < BaseService
include JsonLdHelper
class Result < ActiveModelSerializers::Model
attributes :account, :devices
def initialize(account, devices)
super(
account: account,
devices: devices || [],
)
end
def find(device_id)
@devices.find { |device| device.device_id == device_id }
end
end
class Device < ActiveModelSerializers::Model
attributes :device_id, :name, :identity_key, :fingerprint_key
def initialize(attributes = {})
super(
device_id: attributes[:device_id],
name: attributes[:name],
identity_key: attributes[:identity_key],
fingerprint_key: attributes[:fingerprint_key],
)
@claim_url = attributes[:claim_url]
end
def valid_claim_url?
return false if @claim_url.blank?
begin
parsed_url = Addressable::URI.parse(@claim_url).normalize
rescue Addressable::URI::InvalidURIError
return false
end
%w(http https).include?(parsed_url.scheme) && parsed_url.host.present?
end
end
def call(account)
@account = account
if @account.local?
query_local_devices!
else
query_remote_devices!
end
Result.new(@account, @devices)
end
private
def query_local_devices!
@devices = @account.devices.map { |device| Device.new(device) }
end
def query_remote_devices!
return if @account.devices_url.blank?
json = fetch_resource(@account.devices_url)
return if json['items'].blank?
@devices = as_array(json['items']).map do |device|
Device.new(device_id: device['id'], name: device['name'], identity_key: device.dig('identityKey', 'publicKeyBase64'), fingerprint_key: device.dig('fingerprintKey', 'publicKeyBase64'), claim_url: device['claim'])
end
rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e
Rails.logger.debug { "Querying devices for #{@account.acct} failed: #{e}" }
nil
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
class Ed25519KeyValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
key = Base64.decode64(value)
record.errors.add(attribute, I18n.t('crypto.errors.invalid_key')) unless verified?(key)
end
private
def verified?(key)
Ed25519.validate_key_bytes(key)
rescue ArgumentError
false
end
end

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
class Ed25519SignatureValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
verify_key = Ed25519::VerifyKey.new(Base64.decode64(option_to_value(record, :verify_key)))
signature = Base64.decode64(value)
message = option_to_value(record, :message)
record.errors.add(attribute, I18n.t('crypto.errors.invalid_signature')) unless verified?(verify_key, signature, message)
end
private
def verified?(verify_key, signature, message)
verify_key.verify(signature, message)
rescue Ed25519::VerifyError, ArgumentError
false
end
def option_to_value(record, key)
if options[key].is_a?(Proc)
options[key].call(record)
else
record.public_send(options[key])
end
end
end

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
class PushEncryptedMessageWorker
include Sidekiq::Worker
include Redisable
def perform(encrypted_message_id)
encrypted_message = EncryptedMessage.find(encrypted_message_id)
message = InlineRenderer.render(encrypted_message, nil, :encrypted_message)
timeline_id = "timeline:#{encrypted_message.device.account_id}:#{encrypted_message.device.device_id}"
redis.publish(timeline_id, Oj.dump(event: :encrypted_message, payload: message))
rescue ActiveRecord::RecordNotFound
true
end
end

View File

@ -118,8 +118,7 @@ Doorkeeper.configure do
:'admin:write:domain_blocks', :'admin:write:domain_blocks',
:'admin:write:ip_blocks', :'admin:write:ip_blocks',
:'admin:write:email_domain_blocks', :'admin:write:email_domain_blocks',
:'admin:write:canonical_email_blocks', :'admin:write:canonical_email_blocks'
:crypto
# Change the way client credentials are retrieved from the request object. # Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then

View File

@ -19,7 +19,6 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'CLI' inflect.acronym 'CLI'
inflect.acronym 'DeepL' inflect.acronym 'DeepL'
inflect.acronym 'DSL' inflect.acronym 'DSL'
inflect.acronym 'Ed25519'
inflect.acronym 'JsonLd' inflect.acronym 'JsonLd'
inflect.acronym 'OEmbed' inflect.acronym 'OEmbed'
inflect.acronym 'OStatus' inflect.acronym 'OStatus'

View File

@ -953,7 +953,6 @@ an:
crypto: crypto:
errors: errors:
invalid_key: no ye una clau Ed25519 u Curve25519 valida invalid_key: no ye una clau Ed25519 u Curve25519 valida
invalid_signature: no ye una sinyatura Ed25519 valida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1178,7 +1178,6 @@ ar:
crypto: crypto:
errors: errors:
invalid_key: ليس بمفتاح Ed25519 أو Curve25519 صالح invalid_key: ليس بمفتاح Ed25519 أو Curve25519 صالح
invalid_signature: ليس بتوقيع Ed25519 صالح
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -486,7 +486,6 @@ ast:
crypto: crypto:
errors: errors:
invalid_key: nun ye una clave ed25519 o curve25519 válida invalid_key: nun ye una clave ed25519 o curve25519 válida
invalid_signature: nun ye una clave ed25519 válida
datetime: datetime:
distance_in_words: distance_in_words:
about_x_hours: "%{count} h" about_x_hours: "%{count} h"

View File

@ -1189,7 +1189,6 @@ be:
crypto: crypto:
errors: errors:
invalid_key: гэта не сапраўдны Ed25519 або Curve25519 ключ invalid_key: гэта не сапраўдны Ed25519 або Curve25519 ключ
invalid_signature: гэта не сапраўдная Ed25519 сігнатура
date: date:
formats: formats:
default: "%d.%m.%Y" default: "%d.%m.%Y"

View File

@ -1115,7 +1115,6 @@ bg:
crypto: crypto:
errors: errors:
invalid_key: не е валиден ключ Ed25519 или Curve25519 invalid_key: не е валиден ключ Ed25519 или Curve25519
invalid_signature: не е валиден подпис Ed25519
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1171,7 +1171,6 @@ ca:
crypto: crypto:
errors: errors:
invalid_key: no és una clau Ed25519 o Curve25519 vàlida invalid_key: no és una clau Ed25519 o Curve25519 vàlida
invalid_signature: no és una signatura Ed25519 vàlida
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -608,7 +608,6 @@ ckb:
crypto: crypto:
errors: errors:
invalid_key: کلیلی باوڕپێکراو Ed25519 یان Curve25519 دروست نییە invalid_key: کلیلی باوڕپێکراو Ed25519 یان Curve25519 دروست نییە
invalid_signature: واژووی Ed25519 بڕوادار نییە
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -569,7 +569,6 @@ co:
crypto: crypto:
errors: errors:
invalid_key: ùn hè micca una chjave Ed25519 o Curve25519 valida invalid_key: ùn hè micca una chjave Ed25519 o Curve25519 valida
invalid_signature: ùn hè micca una firma Ed25519 valida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1149,7 +1149,6 @@ cs:
crypto: crypto:
errors: errors:
invalid_key: není platný klíč Ed25519 nebo Curve25519 invalid_key: není platný klíč Ed25519 nebo Curve25519
invalid_signature: není platný podpis typu Ed25519
date: date:
formats: formats:
default: "%d. %b %Y" default: "%d. %b %Y"

View File

@ -1246,7 +1246,6 @@ cy:
crypto: crypto:
errors: errors:
invalid_key: ddim yn allwedd Ed25519 na Curve25519 dilys invalid_key: ddim yn allwedd Ed25519 na Curve25519 dilys
invalid_signature: ddim yn llofnod Ed25519 dilys
date: date:
formats: formats:
default: "%b %d %Y" default: "%b %d %Y"

View File

@ -1174,7 +1174,6 @@ da:
crypto: crypto:
errors: errors:
invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøgle invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøgle
invalid_signature: er ikke en gylidig Ed25519-signatur
date: date:
formats: formats:
default: "%d. %b %Y" default: "%d. %b %Y"

View File

@ -1174,7 +1174,6 @@ de:
crypto: crypto:
errors: errors:
invalid_key: ist kein gültiger Ed25519- oder Curve25519-Schlüssel invalid_key: ist kein gültiger Ed25519- oder Curve25519-Schlüssel
invalid_signature: ist keine gültige Ed25519-Signatur
date: date:
formats: formats:
default: "%d. %b %Y" default: "%d. %b %Y"

View File

@ -1128,7 +1128,6 @@ el:
crypto: crypto:
errors: errors:
invalid_key: δεν είναι έγκυρο κλειδί Ed25519 ή Curve25519 invalid_key: δεν είναι έγκυρο κλειδί Ed25519 ή Curve25519
invalid_signature: δεν είναι έγκυρη υπογραφή Ed25519
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1174,7 +1174,6 @@ en-GB:
crypto: crypto:
errors: errors:
invalid_key: is not a valid Ed25519 or Curve25519 key invalid_key: is not a valid Ed25519 or Curve25519 key
invalid_signature: is not a valid Ed25519 signature
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1174,7 +1174,6 @@ en:
crypto: crypto:
errors: errors:
invalid_key: is not a valid Ed25519 or Curve25519 key invalid_key: is not a valid Ed25519 or Curve25519 key
invalid_signature: is not a valid Ed25519 signature
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1041,7 +1041,6 @@ eo:
crypto: crypto:
errors: errors:
invalid_key: 올바른 Ed25519 혹은 Curve25519 키가 아닙니다 invalid_key: 올바른 Ed25519 혹은 Curve25519 키가 아닙니다
invalid_signature: 올바른 Ed25519 시그니처가 아닙니다
date: date:
formats: formats:
default: "%Y-%b-%d" default: "%Y-%b-%d"

View File

@ -1174,7 +1174,6 @@ es-AR:
crypto: crypto:
errors: errors:
invalid_key: no es una clave Ed25519 o Curve25519 válida invalid_key: no es una clave Ed25519 o Curve25519 válida
invalid_signature: no es una firma Ed25519 válida
date: date:
formats: formats:
default: "%d de %b de %Y" default: "%d de %b de %Y"

View File

@ -1174,7 +1174,6 @@ es-MX:
crypto: crypto:
errors: errors:
invalid_key: no es una clave Ed25519 o Curve25519 válida invalid_key: no es una clave Ed25519 o Curve25519 válida
invalid_signature: no es una firma Ed25519 válida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1174,7 +1174,6 @@ es:
crypto: crypto:
errors: errors:
invalid_key: no es una clave Ed25519 o Curve25519 válida invalid_key: no es una clave Ed25519 o Curve25519 válida
invalid_signature: no es una firma Ed25519 válida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1174,7 +1174,6 @@ et:
crypto: crypto:
errors: errors:
invalid_key: ei ole õige Ed25519 ega Curve25519 võti invalid_key: ei ole õige Ed25519 ega Curve25519 võti
invalid_signature: ei ole õige Ed25519 allkiri
date: date:
formats: formats:
default: "%d. %b %Y" default: "%d. %b %Y"

View File

@ -1091,7 +1091,6 @@ eu:
crypto: crypto:
errors: errors:
invalid_key: ez da baliozko Ed25519 edo Curve25519 gakoa invalid_key: ez da baliozko Ed25519 edo Curve25519 gakoa
invalid_signature: ez da baliozko Ed25519 sinadura
date: date:
formats: formats:
default: "%Y(e)ko %b %d" default: "%Y(e)ko %b %d"

View File

@ -971,7 +971,6 @@ fa:
crypto: crypto:
errors: errors:
invalid_key: یک کلید معتبر Ed25519 یا Curve25519 نیست invalid_key: یک کلید معتبر Ed25519 یا Curve25519 نیست
invalid_signature: یک امضای معتبر Ed25519 نیست
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1174,7 +1174,6 @@ fi:
crypto: crypto:
errors: errors:
invalid_key: ei ole kelvollinen Ed25519- tai Curve25519-avain invalid_key: ei ole kelvollinen Ed25519- tai Curve25519-avain
invalid_signature: ei ole kelvollinen Ed25519-allekirjoitus
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1174,7 +1174,6 @@ fo:
crypto: crypto:
errors: errors:
invalid_key: er ikki ein gildur Ed25519 ella Curve25519 lykil invalid_key: er ikki ein gildur Ed25519 ella Curve25519 lykil
invalid_signature: er ikki ein gildug Ed25519 undirskrift
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1175,7 +1175,6 @@ fr-CA:
crypto: crypto:
errors: errors:
invalid_key: nest pas une clé Ed25519 ou Curve25519 valide invalid_key: nest pas une clé Ed25519 ou Curve25519 valide
invalid_signature: nest pas une signature Ed25519 valide
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1175,7 +1175,6 @@ fr:
crypto: crypto:
errors: errors:
invalid_key: nest pas une clé Ed25519 ou Curve25519 valide invalid_key: nest pas une clé Ed25519 ou Curve25519 valide
invalid_signature: nest pas une signature Ed25519 valide
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1164,7 +1164,6 @@ fy:
crypto: crypto:
errors: errors:
invalid_key: is gjin jildige Ed25519- of Curve25519-kaai invalid_key: is gjin jildige Ed25519- of Curve25519-kaai
invalid_signature: is gjin jildige Ed25519-hantekening
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1228,7 +1228,6 @@ ga:
crypto: crypto:
errors: errors:
invalid_key: nach eochair bhailí Ed25519 nó Curve25519 í invalid_key: nach eochair bhailí Ed25519 nó Curve25519 í
invalid_signature: nach síniú bailí Ed25519 é
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1210,7 +1210,6 @@ gd:
crypto: crypto:
errors: errors:
invalid_key: " chan e iuchair Ed25519 no Curve25519 dhligheach a th ann" invalid_key: " chan e iuchair Ed25519 no Curve25519 dhligheach a th ann"
invalid_signature: " chan e soidhneadh Ed25519 dligheach a th ann"
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1174,7 +1174,6 @@ gl:
crypto: crypto:
errors: errors:
invalid_key: non é unha chave Ed25519 ou Curve25519 válida invalid_key: non é unha chave Ed25519 ou Curve25519 válida
invalid_signature: non é unha sinatura Ed25519 válida
date: date:
formats: formats:
default: "%d %b, %Y" default: "%d %b, %Y"

View File

@ -1210,7 +1210,6 @@ he:
crypto: crypto:
errors: errors:
invalid_key: זהו לא מפתח Ed25519 או Curve25519 קביל invalid_key: זהו לא מפתח Ed25519 או Curve25519 קביל
invalid_signature: היא לא חתימת Ed25519 קבילה
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1174,7 +1174,6 @@ hu:
crypto: crypto:
errors: errors:
invalid_key: érvénytelen Ed25519 vagy Curve25519 kulcs invalid_key: érvénytelen Ed25519 vagy Curve25519 kulcs
invalid_signature: érvénytelen Ed25519 aláírás
date: date:
formats: formats:
default: "%Y. %b %d." default: "%Y. %b %d."

View File

@ -495,7 +495,6 @@ hy:
crypto: crypto:
errors: errors:
invalid_key: անվաւեր Ed25519 կամ Curve25519 բանալի invalid_key: անվաւեր Ed25519 կամ Curve25519 բանալի
invalid_signature: անվաւեր Ed25519 բանալի
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1158,7 +1158,6 @@ ia:
crypto: crypto:
errors: errors:
invalid_key: non es un clave Ed25519 o Curve25519 valide invalid_key: non es un clave Ed25519 o Curve25519 valide
invalid_signature: non es un signatura Ed25519 valide
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -936,7 +936,6 @@ id:
crypto: crypto:
errors: errors:
invalid_key: bukan kunci Ed25519 atau Curve25519 yang valid invalid_key: bukan kunci Ed25519 atau Curve25519 yang valid
invalid_signature: bukan tanda tangan Ed25519 yang valid
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1089,7 +1089,6 @@ ie:
crypto: crypto:
errors: errors:
invalid_key: ne es un valid clave Ed25519 o Curve25519 invalid_key: ne es un valid clave Ed25519 o Curve25519
invalid_signature: ne es un valid signatura Ed25519
date: date:
formats: formats:
default: "%d.%m.%Y" default: "%d.%m.%Y"

View File

@ -1064,7 +1064,6 @@ io:
crypto: crypto:
errors: errors:
invalid_key: ne esas valida klefo Ed25519 o Curve25519 invalid_key: ne esas valida klefo Ed25519 o Curve25519
invalid_signature: ne esas valida parafo Ed25519
date: date:
formats: formats:
default: "%d %b, %Y" default: "%d %b, %Y"

View File

@ -1178,7 +1178,6 @@ is:
crypto: crypto:
errors: errors:
invalid_key: er ekki gildur Ed25519 eða Curve25519-lykill invalid_key: er ekki gildur Ed25519 eða Curve25519-lykill
invalid_signature: er ekki gild Ed25519 undirritun
date: date:
formats: formats:
default: "%d. %b, %Y" default: "%d. %b, %Y"

View File

@ -1176,7 +1176,6 @@ it:
crypto: crypto:
errors: errors:
invalid_key: non è una chiave Ed25519 o Curve25519 valida invalid_key: non è una chiave Ed25519 o Curve25519 valida
invalid_signature: non è una firma Ed25519 valida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1146,7 +1146,6 @@ ja:
crypto: crypto:
errors: errors:
invalid_key: 有効なEd25519またはCurve25519キーではありません invalid_key: 有効なEd25519またはCurve25519キーではありません
invalid_signature: 有効なEd25519署名ではありません
date: date:
formats: formats:
default: "%Y年%m月%d日" default: "%Y年%m月%d日"

View File

@ -1151,7 +1151,6 @@ ko:
crypto: crypto:
errors: errors:
invalid_key: 유효하지 않은 Ed25519 또는 Curve25519 키 invalid_key: 유효하지 않은 Ed25519 또는 Curve25519 키
invalid_signature: 유효하지 않은 Ed25519 서명
date: date:
formats: formats:
default: "%Y-%m-%d" default: "%Y-%m-%d"

View File

@ -950,7 +950,6 @@ ku:
crypto: crypto:
errors: errors:
invalid_key: ed25519 ne derbasdare ne jî Curve25519 kilîta invalid_key: ed25519 ne derbasdare ne jî Curve25519 kilîta
invalid_signature: Ed25519 ne îmzeyek derbasdar e
date: date:
formats: formats:
default: "%b%d%Y" default: "%b%d%Y"

View File

@ -1122,7 +1122,6 @@ lad:
crypto: crypto:
errors: errors:
invalid_key: no es una yave Ed25519 o Curve25519 valida invalid_key: no es una yave Ed25519 o Curve25519 valida
invalid_signature: no es una firma Ed25519 valida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1140,7 +1140,6 @@ lv:
crypto: crypto:
errors: errors:
invalid_key: nav derīga Ed25519 vai Curve25519 atslēga invalid_key: nav derīga Ed25519 vai Curve25519 atslēga
invalid_signature: nav derīgs Ed25519 paraksts
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1051,7 +1051,6 @@ ms:
crypto: crypto:
errors: errors:
invalid_key: bukan kunci Ed25519 atau Curve25519 yang sah invalid_key: bukan kunci Ed25519 atau Curve25519 yang sah
invalid_signature: bukan tandatangan Ed25519 yang sah
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1044,7 +1044,6 @@ my:
crypto: crypto:
errors: errors:
invalid_key: မှန်ကန်သော Ed25519 သို့မဟုတ် Curve25519 ကီး မဟုတ်ပါ။ invalid_key: မှန်ကန်သော Ed25519 သို့မဟုတ် Curve25519 ကီး မဟုတ်ပါ။
invalid_signature: မှန်ကန်သော Ed25519 လက်မှတ်မဟုတ်ပါ
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -1174,7 +1174,6 @@ nl:
crypto: crypto:
errors: errors:
invalid_key: is geen geldige Ed25519- of Curve25519-sleutel invalid_key: is geen geldige Ed25519- of Curve25519-sleutel
invalid_signature: is geen geldige Ed25519-handtekening
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1174,7 +1174,6 @@ nn:
crypto: crypto:
errors: errors:
invalid_key: er ikkje ein gild Ed25519 eller Curve25519 nykel invalid_key: er ikkje ein gild Ed25519 eller Curve25519 nykel
invalid_signature: er ikkje ein gild Ed25519-signatur
date: date:
formats: formats:
default: "%d. %b, %Y" default: "%d. %b, %Y"

View File

@ -1083,7 +1083,6 @@
crypto: crypto:
errors: errors:
invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøkkel invalid_key: er ikke en gyldig Ed25519- eller Curve25519-nøkkel
invalid_signature: er ikke en gyldig Ed25519-signatur
date: date:
formats: formats:
default: "%d. %b, %Y" default: "%d. %b, %Y"

View File

@ -1210,7 +1210,6 @@ pl:
crypto: crypto:
errors: errors:
invalid_key: nie jest prawidłowym kluczem Ed25519 lub Curve25519 invalid_key: nie jest prawidłowym kluczem Ed25519 lub Curve25519
invalid_signature: nie jest prawidłowym podpisem Ed25519
date: date:
formats: formats:
default: "%d. %b %Y" default: "%d. %b %Y"

View File

@ -1174,7 +1174,6 @@ pt-BR:
crypto: crypto:
errors: errors:
invalid_key: não é uma chave Ed25519 ou Curve25519 válida invalid_key: não é uma chave Ed25519 ou Curve25519 válida
invalid_signature: não é uma assinatura Ed25519 válida
date: date:
formats: formats:
default: "%d %b, %Y" default: "%d %b, %Y"

View File

@ -1174,7 +1174,6 @@ pt-PT:
crypto: crypto:
errors: errors:
invalid_key: não é uma chave Ed25519 ou Curve25519 válida invalid_key: não é uma chave Ed25519 ou Curve25519 válida
invalid_signature: não é uma assinatura Ed25519 válida
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1129,7 +1129,6 @@ ru:
crypto: crypto:
errors: errors:
invalid_key: не является допустимым Ed25519 или Curve25519 ключом invalid_key: не является допустимым Ed25519 или Curve25519 ключом
invalid_signature: не является допустимой Ed25519 подписью
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -693,7 +693,6 @@ sc:
crypto: crypto:
errors: errors:
invalid_key: no est una crae Ed25519 o Curve25519 vàlida invalid_key: no est una crae Ed25519 o Curve25519 vàlida
invalid_signature: no est una firma Ed25519 vàlida
date: date:
formats: formats:
default: "%d %b, %Y" default: "%d %b, %Y"

View File

@ -940,7 +940,6 @@ sco:
crypto: crypto:
errors: errors:
invalid_key: isnae a valid Ed25519 or Curve25519 key invalid_key: isnae a valid Ed25519 or Curve25519 key
invalid_signature: isnae a valid Ed25519 signature
date: date:
formats: formats:
default: "%b %d, %Y" default: "%b %d, %Y"

View File

@ -829,7 +829,6 @@ si:
crypto: crypto:
errors: errors:
invalid_key: වලංගු Ed25519 හෝ Curve25519 යතුරක් නොවේ invalid_key: වලංගු Ed25519 හෝ Curve25519 යතුරක් නොවේ
invalid_signature: වලංගු Ed25519 අත්සනක් නොවේ
date: date:
formats: formats:
default: "%Y %b %d" default: "%Y %b %d"

View File

@ -1196,7 +1196,6 @@ sl:
crypto: crypto:
errors: errors:
invalid_key: ni veljaven ključ Ed25519 ali Curve25519 invalid_key: ni veljaven ključ Ed25519 ali Curve25519
invalid_signature: ni veljaven podpis Ed25519
date: date:
formats: formats:
default: "%d %b %Y" default: "%d %b %Y"

View File

@ -1166,7 +1166,6 @@ sq:
crypto: crypto:
errors: errors:
invalid_key: sështë kyç Ed25519 ose Curve25519 i vlefshëm invalid_key: sështë kyç Ed25519 ose Curve25519 i vlefshëm
invalid_signature: sështë nënshkrim Ed25519 i vlefshëm
date: date:
formats: formats:
default: "%d %b, %Y" default: "%d %b, %Y"

View File

@ -1110,7 +1110,6 @@ sr-Latn:
crypto: crypto:
errors: errors:
invalid_key: nije validan Ed25519 ili Curve25519 ključ invalid_key: nije validan Ed25519 ili Curve25519 ključ
invalid_signature: nije validan Ed25519 potpis
date: date:
formats: formats:
default: "%d. %b. %Y." default: "%d. %b. %Y."

Some files were not shown because too many files have changed in this diff Show More