From 73f041f2b1e141af2d3fb0c4186cf5d954db20b4 Mon Sep 17 00:00:00 2001 From: Carmine Di Monaco Date: Wed, 6 Nov 2024 16:40:42 +0100 Subject: [PATCH] Elixir contracts signing and validation checking --- elixir/lib/contracts.ex | 229 ++++++--- elixir/mix.exs | 2 +- elixir/test/contracts_test.exs | 477 +++++++++++++++--- elixir/test/support/fixtures/test.private.pem | 28 + elixir/test/support/fixtures/test.public.pem | 9 + 5 files changed, 602 insertions(+), 143 deletions(-) create mode 100644 elixir/test/support/fixtures/test.private.pem create mode 100644 elixir/test/support/fixtures/test.public.pem diff --git a/elixir/lib/contracts.ex b/elixir/lib/contracts.ex index a90597e..012eeab 100644 --- a/elixir/lib/contracts.ex +++ b/elixir/lib/contracts.ex @@ -5,6 +5,8 @@ defmodule Trento.Contracts do require Logger + @default_event_validity 20 + @doc """ Return the content type of contracts """ @@ -14,10 +16,62 @@ defmodule Trento.Contracts do Encode and wrap an event struct to a protobuf CloudEvent. """ @spec to_event(struct(), Keyword.t()) :: binary() - def to_event(%mod{} = struct, opts \\ []) do + def to_event(%{} = struct, opts \\ []) do + struct + |> build_cloud_event(opts) + |> CloudEvents.CloudEvent.encode() + end + + @doc """ + Encode and wrap an event struct to a signed protobuf Cloudevent. + Accepts the private key as a pem file content. + Accepts the same options as to_event/2 + """ + def to_signed_event(struct, pem_private_key, opts \\ []) do + struct + |> build_cloud_event(opts) + |> add_signature(pem_private_key) + |> CloudEvents.CloudEvent.encode() + end + + @doc """ + Decode and unwrap a protobuf CloudEvent to an event struct. + """ + @spec from_event(binary()) :: + {:ok, struct()} + | {:error, :decoding_error} + | {:error, :invalid_envelope} + | {:error, :unknown_event} + | {:error, :event_expired} + def from_event(value) do + with {:ok, type, cloud_event} <- decode_cloud_event(value), + {:ok, cloud_event} <- verify_event_validity(cloud_event) do + decode_trento_event(type, cloud_event) + end + end + + @doc """ + Decode and unwrap a signed protobuf CloudEvent to an event struct. + """ + @spec from_signed_event(binary(), binary()) :: + {:ok, struct()} + | {:error, :decoding_error} + | {:error, :invalid_envelope} + | {:error, :unknown_event} + | {:error, :event_expired} + | {:error, :invalid_event_signature} + def from_signed_event(value, public_key) do + with {:ok, event_type, event_data} <- decode_cloud_event(value), + {:ok, event_data} <- verify_event_signature(event_data, public_key), + {:ok, event_data} <- verify_event_validity(event_data) do + decode_trento_event(event_type, event_data) + end + end + + defp build_cloud_event(%mod{} = struct, opts) do id = Keyword.get(opts, :id, UUID.uuid4()) source = Keyword.get(opts, :source, "trento") - private_key = Keyword.get(opts, :private_key, "") + validity_in_seconds = Keyword.get(opts, :validity_in_seconds, @default_event_validity) time = Keyword.get( @@ -26,36 +80,68 @@ defmodule Trento.Contracts do DateTime.utc_now() ) + expiration = DateTime.add(time, validity_in_seconds) + time_attr = %Google.Protobuf.Timestamp{seconds: time |> DateTime.to_unix()} + expire_at_attr = %Google.Protobuf.Timestamp{seconds: expiration |> DateTime.to_unix()} + data = Protobuf.Encoder.encode(struct) - cloud_event = %CloudEvents.CloudEvent{ + %CloudEvents.CloudEvent{ data: {:proto_data, %Google.Protobuf.Any{value: data, type_url: get_type(mod)}}, spec_version: "1.0", type: get_type(mod), id: id, attributes: %{ "time" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_timestamp, time_attr}}, + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, expire_at_attr} + } }, source: source - } |> add_signature(private_key) + } + end + + defp add_signature( + %CloudEvents.CloudEvent{ + data: {:proto_data, %Google.Protobuf.Any{value: data}}, + attributes: + %{ + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time}} + }, + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at}} + } + } = current_attrs + } = event, + private_key_content + ) do + signing_key = + private_key_content + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() - CloudEvents.CloudEvent.encode(cloud_event) + time_str = Integer.to_string(time) + signature = :public_key.sign("#{data}#{time_str}#{expire_at}", :sha256, signing_key) + + %CloudEvents.CloudEvent{ + event + | attributes: + Map.put( + current_attrs, + "signature", + %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + ) + } end - @doc """ - Decode and unwrap a protobuf CloudEvent to an event struct. - """ - @spec from_event(binary()) :: - {:ok, struct()} - | {:error, :decoding_error} - | {:error, :invalid_envelope} - | {:error, :event_not_found} - def from_event(value) do + defp decode_cloud_event(data) do try do - case CloudEvents.CloudEvent.decode(value) do - %{type: type, data: {:proto_data, %Google.Protobuf.Any{value: data}}} -> - decode(type, data) + case CloudEvents.CloudEvent.decode(data) do + %{type: type, data: {:proto_data, %Google.Protobuf.Any{value: _data}}} = cloud_event -> + {:ok, type, cloud_event} event -> Logger.error("Invalid cloud event: #{inspect(event)}") @@ -70,7 +156,63 @@ defmodule Trento.Contracts do end end - defp decode(type, data) do + defp verify_event_signature( + %CloudEvents.CloudEvent{ + data: {:proto_data, %Google.Protobuf.Any{value: data}}, + attributes: %{ + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time}} + }, + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + } + } = event, + public_key + ) do + signing_key = + public_key + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() + + event_signature_valid? = + :public_key.verify("#{data}#{time}#{expire_at}", :sha256, signature, signing_key) + + if event_signature_valid? do + {:ok, event} + else + {:error, :invalid_event_signature} + end + end + + defp verify_event_signature(_, _), do: {:error, :invalid_event_signature} + + defp verify_event_validity( + %CloudEvents.CloudEvent{ + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + } + } + } = event + ) do + expire_at = DateTime.from_unix!(expire_at_ts) + event_valid? = DateTime.compare(DateTime.utc_now(), expire_at) == :lt + + if event_valid? do + {:ok, event} + else + {:error, :event_expired} + end + end + + defp verify_event_validity(_), do: {:error, :event_expired} + + defp decode_trento_event(type, %CloudEvents.CloudEvent{ + data: {:proto_data, %Google.Protobuf.Any{value: data}} + }) do try do module_name = Macro.camelize(type) module = Module.safe_concat([module_name]) @@ -81,58 +223,11 @@ defmodule Trento.Contracts do end end + defp decode_trento_event(_, _), do: {:error, :unknown_event} + defp get_type(mod) do mod |> Atom.to_string() |> String.replace("Elixir.", "") end - - defp add_signature(event, ""), do: event - - defp add_signature( - %CloudEvents.CloudEvent{ - data: {:proto_data, %Google.Protobuf.Any{value: data}}, - attributes: %{ - "time" => %CloudEvents.CloudEventAttributeValue{ - attr: {:ce_timestamp, %{seconds: time}} - } - } = current_attrs - } = event, private_key) do - - time_str = Integer.to_string(time) - signature = :public_key.sign(data <> time_str, :sha256, private_key) - - %CloudEvents.CloudEvent{ - event | - attributes: Map.put( - current_attrs, - "signature", - %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} - ) - } - end - - # def add_signature(event, private_key) do - # decoded_event = %{ - # data: {:proto_data, %Google.Protobuf.Any{value: data}}, - # attributes: %{ - # "time" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_timestamp, time}} - # } = current_attrs - # } = CloudEvents.CloudEvent.decode(event) - - # %{seconds: seconds} = time - # time_str = Integer.to_string(seconds) - # signature = :public_key.sign(data <> time_str, :sha256, private_key) - - # updated_event = %CloudEvents.CloudEvent{ - # decoded_event | - # attributes: Map.put( - # current_attrs, - # "signature", - # %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} - # ) - # } - - # CloudEvents.CloudEvent.encode(updated_event) - # end end diff --git a/elixir/mix.exs b/elixir/mix.exs index 3f623ec..6b89225 100644 --- a/elixir/mix.exs +++ b/elixir/mix.exs @@ -15,7 +15,7 @@ defmodule Proto.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger] + extra_applications: [:logger, :public_key, :crypto] ] end diff --git a/elixir/test/contracts_test.exs b/elixir/test/contracts_test.exs index 7e338b1..0ea8f80 100644 --- a/elixir/test/contracts_test.exs +++ b/elixir/test/contracts_test.exs @@ -3,90 +3,417 @@ defmodule Trento.ContractsTest do alias CloudEvents.CloudEvent - test "should decode to the right struct" do - event_id = UUID.uuid4() - event = %Test.Event{id: event_id} - - cloudevent = %CloudEvent{ - data: - {:proto_data, - %Google.Protobuf.Any{ - __unknown_fields__: [], - type_url: "test.Event", - value: Test.Event.encode(event) - }}, - id: UUID.uuid4(), - source: "wandalorian", - spec_version: "1.0", - type: "test.Event" - } - - encoded_cloudevent = CloudEvent.encode(cloudevent) - - assert {:ok, %Test.Event{id: ^event_id}} = Trento.Contracts.from_event(encoded_cloudevent) - end + describe "message with signature" do + setup do + private_key = File.read!("test/support/fixtures/test.private.pem") + public_key = File.read!("test/support/fixtures/test.public.pem") + + %{private_key: private_key, public_key: public_key} + end + + test "should decode to the right struct", %{public_key: public_key, private_key: private_key} do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} + + time = DateTime.utc_now() + time_ts = DateTime.to_unix(time) + + expire_at = DateTime.add(time, 60) + expire_at_ts = DateTime.to_unix(expire_at) - test "should encode to the right struct" do - event = %Test.Event{id: UUID.uuid4()} - cloudevent_id = UUID.uuid4() - time = DateTime.utc_now() - time_attr = %Google.Protobuf.Timestamp{seconds: time |> DateTime.to_unix()} - - cloudevent = %CloudEvent{ - data: - {:proto_data, - %Google.Protobuf.Any{ - __unknown_fields__: [], - type_url: "Test.Event", - value: Test.Event.encode(event) - }}, - id: cloudevent_id, - source: "wandalorian", - spec_version: "1.0", - type: "Test.Event", - attributes: %{ - "time" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_timestamp, time_attr}} + message_content = Test.Event.encode(event) + + signing_key = + private_key + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() + + signature = + :public_key.sign("#{message_content}#{time_ts}#{expire_at_ts}", :sha256, signing_key) + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "test.Event", + value: message_content + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + }, + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time_ts}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "test.Event" } - } - encoded_cloudevent = CloudEvent.encode(cloudevent) + encoded_cloudevent = CloudEvent.encode(cloudevent) - assert encoded_cloudevent == - Trento.Contracts.to_event(event, - id: cloudevent_id, - source: "wandalorian", - time: time - ) - end + assert {:ok, %Test.Event{id: ^event_id}} = + Trento.Contracts.from_signed_event(encoded_cloudevent, public_key) + end - test "should return error if the event is not wrapped in a CloudEvent" do - event = Test.Event.encode(%Test.Event{id: UUID.uuid4()}) + test "should encode to the right struct", %{private_key: private_key} do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} - assert {:error, :invalid_envelope} = Trento.Contracts.from_event(event) - end + time = DateTime.utc_now() + time_ts = DateTime.to_unix(time) + + expire_at = DateTime.add(time, 20) + expire_at_ts = DateTime.to_unix(expire_at) + + message_content = Test.Event.encode(event) + + signing_key = + private_key + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() + + signature = + :public_key.sign("#{message_content}#{time_ts}#{expire_at_ts}", :sha256, signing_key) + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "Test.Event", + value: message_content + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + }, + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time_ts}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + }, + id: event_id, + source: "wandalorian", + spec_version: "1.0", + type: "Test.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert encoded_cloudevent == + Trento.Contracts.to_signed_event( + event, + private_key, + id: event_id, + source: "wandalorian", + time: time + ) + end + + test "should return error if the event is not wrapped in a CloudEvent", %{ + public_key: public_key + } do + event = Test.Event.encode(%Test.Event{id: UUID.uuid4()}) + + assert {:error, :invalid_envelope} = Trento.Contracts.from_signed_event(event, public_key) + end + + test "should return error if the could not be decoded", %{ + public_key: public_key + } do + event = <<0, 0>> + + assert {:error, :decoding_error} = Trento.Contracts.from_signed_event(event, public_key) + end + + test "should return error if the event type is unknown", %{ + private_key: private_key, + public_key: public_key + } do + time = DateTime.utc_now() + time_ts = DateTime.to_unix(time) + + expire_at = DateTime.add(time, 60) + expire_at_ts = DateTime.to_unix(expire_at) + + message_content = <<0, 0, 0, 0, 0, 0, 0, 0>> + + signing_key = + private_key + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() + + signature = + :public_key.sign("#{message_content}#{time_ts}#{expire_at_ts}", :sha256, signing_key) + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "unknown.Event", + value: message_content + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + }, + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time_ts}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "unknown.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert {:error, :unknown_event} = + Trento.Contracts.from_signed_event(encoded_cloudevent, public_key) + end + + test "should return error if the event is expired", %{ + private_key: private_key, + public_key: public_key + } do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} + + time = DateTime.utc_now() + time_ts = DateTime.to_unix(time) + + expire_at = DateTime.add(time, -60) + expire_at_ts = DateTime.to_unix(expire_at) + + message_content = Test.Event.encode(event) - test "should return error if the could not be decoded" do - event = <<0, 0>> + signing_key = + private_key + |> :public_key.pem_decode() + |> Enum.at(0) + |> :public_key.pem_entry_decode() - assert {:error, :decoding_error} = Trento.Contracts.from_event(event) + signature = + :public_key.sign("#{message_content}#{time_ts}#{expire_at_ts}", :sha256, signing_key) + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "test.Event", + value: message_content + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + }, + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time_ts}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "test.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert {:error, :event_expired} = + Trento.Contracts.from_signed_event(encoded_cloudevent, public_key) + end + + test "should return error if the event signature is not valid", %{ + public_key: public_key + } do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} + + time = DateTime.utc_now() + time_ts = DateTime.to_unix(time) + + expire_at = DateTime.add(time, -60) + expire_at_ts = DateTime.to_unix(expire_at) + + message_content = Test.Event.encode(event) + signature = "invalidsignature" + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "test.Event", + value: message_content + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: expire_at_ts}} + }, + "time" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: time_ts}} + }, + "signature" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_bytes, signature}} + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "test.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert {:error, :invalid_event_signature} = + Trento.Contracts.from_signed_event(encoded_cloudevent, public_key) + end end - test "should return error if the event type is unknown" do - cloudevent = %CloudEvent{ - data: - {:proto_data, - %Google.Protobuf.Any{ - __unknown_fields__: [], - type_url: "Unknown.Event", - value: <<0, 0, 0, 0, 0, 0, 0, 0>> - }}, - id: UUID.uuid4(), - source: "wandalorian", - spec_version: "1.0", - type: "Unknown.Event" - } - - assert {:error, :unknown_event} = - cloudevent |> CloudEvent.encode() |> Trento.Contracts.from_event() + describe "message without signature" do + test "should decode to the right struct" do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} + time = DateTime.utc_now() + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "test.Event", + value: Test.Event.encode(event) + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: DateTime.add(time, 60) |> DateTime.to_unix()}} + } + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "test.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert {:ok, %Test.Event{id: ^event_id}} = Trento.Contracts.from_event(encoded_cloudevent) + end + + test "should encode to the right struct" do + event = %Test.Event{id: UUID.uuid4()} + cloudevent_id = UUID.uuid4() + time = DateTime.utc_now() + time_attr = %Google.Protobuf.Timestamp{seconds: time |> DateTime.to_unix()} + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "Test.Event", + value: Test.Event.encode(event) + }}, + id: cloudevent_id, + source: "wandalorian", + spec_version: "1.0", + type: "Test.Event", + attributes: %{ + "time" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_timestamp, time_attr}}, + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: DateTime.add(time, 20) |> DateTime.to_unix()}} + } + } + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert encoded_cloudevent == + Trento.Contracts.to_event(event, + id: cloudevent_id, + source: "wandalorian", + time: time + ) + end + + test "should return error if the event is not wrapped in a CloudEvent" do + event = Test.Event.encode(%Test.Event{id: UUID.uuid4()}) + + assert {:error, :invalid_envelope} = Trento.Contracts.from_event(event) + end + + test "should return error if the could not be decoded" do + event = <<0, 0>> + + assert {:error, :decoding_error} = Trento.Contracts.from_event(event) + end + + test "should return error if the event type is unknown" do + time = DateTime.utc_now() + time_attr = %Google.Protobuf.Timestamp{seconds: time |> DateTime.to_unix()} + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "Unknown.Event", + value: <<0, 0, 0, 0, 0, 0, 0, 0>> + }}, + attributes: %{ + "time" => %CloudEvents.CloudEventAttributeValue{attr: {:ce_timestamp, time_attr}}, + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: DateTime.add(time, 20) |> DateTime.to_unix()}} + } + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "Unknown.Event" + } + + assert {:error, :unknown_event} = + cloudevent |> CloudEvent.encode() |> Trento.Contracts.from_event() + end + + test "should return error if the event is expired" do + event_id = UUID.uuid4() + event = %Test.Event{id: event_id} + time = DateTime.utc_now() + + cloudevent = %CloudEvent{ + data: + {:proto_data, + %Google.Protobuf.Any{ + __unknown_fields__: [], + type_url: "test.Event", + value: Test.Event.encode(event) + }}, + attributes: %{ + "expire_at" => %CloudEvents.CloudEventAttributeValue{ + attr: {:ce_timestamp, %{seconds: DateTime.add(time, -10) |> DateTime.to_unix()}} + } + }, + id: UUID.uuid4(), + source: "wandalorian", + spec_version: "1.0", + type: "test.Event" + } + + encoded_cloudevent = CloudEvent.encode(cloudevent) + + assert {:error, :event_expired} = Trento.Contracts.from_event(encoded_cloudevent) + end end end diff --git a/elixir/test/support/fixtures/test.private.pem b/elixir/test/support/fixtures/test.private.pem new file mode 100644 index 0000000..4ce5fde --- /dev/null +++ b/elixir/test/support/fixtures/test.private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCoA8U3VyKd2ScT +EtYCBcdidOm7ZJoaAPPl0wjRheZEBQypCJzB1jmH+lucUnVtetHeOhzKhved3IP/ +Rl+EWrGEY1glXROEuBN+imtbbdX6En2dsXYU78jI+emrx0My957w254+9sw3sDUz +SZxYcURJ1uB0dUFnTCaHtRG+Rw0dNH25hDwXC2eF6HFMrSd++VV8edzjGihCYDZ+ +wp8ZtYUHHSJZfD5cROXJOMoSIButoxNewt11vQnGPkc2CZGAvmhASuzVMXE2CvRt +ynLi7uvFSdTtFGUrfXpYM3tsEPqz5jEHjEpslSIS5B8XfYsmXM7P/t46hRHYzUIF +72VQvHmfAgMBAAECggEAEA9gNCSDnhQKTKPxyfq0buBfETcSFec+aRsNjj2e36Sd +67Bzj4HDORDA3gXxzJe/ZN8oLmHNXI0eHvIiojurGU0HuXaWrYqM7n0buv8aQ4+t +6ztx3pl3F2LwJ6fh8AbEbiCIFVuw9KfGxhLEu52EGgzMosUsjcu1KSaxPf97rO1W +STxdw3IAHfxQWv2TAFzoxexA0C5HgSfS5B6fOpO7Hf6cYH3eIuiMztrSlZFcB9Rn +0b8YWPSkVBboY5jLGnT9bbuh+PVcEQgZsg0azeYn1dr5Puq3YeGJDsoZTcLVlTcy ++vHgoVNh/xjxlnp/Nd4zui3DxELKhIqr4u+8xT3CVQKBgQDhDPjl1vg9Iq1KxbnW +HRM5EIT4eeSt6yGhjkcGK4UXgseDa6fvpHOEnPApQtfERHQCpfWvaP3rV9K1Gxv5 +l1TVkOZBEC4PcCjdZOJ6UGvylerobWJaMY8ktjnf9caZ66A1g/Xrl+e3XzklKXTF +lXDoiW2V6mOtO963OO+AMUJYIwKBgQC/HtFMU+d1K+cD+86uvdQqZ4rGryqUalsf +zTcx+51yvQvIXLT/4LdhZtFYXDVgQB3vzHtIPgfJN8tRmWs5qplJq5xXZlCi9RiY +SQLoNKVaE3J+foMOXTdiL3wbQNVOP3cyVCtM8aZ6G9srUY7XvHeIMDwpmg2fkWEx +Qr9hfz5SVQKBgQCxKSwaiuK6Bb1TWlnTwumqB2YV0KcDflDoQ/+HTOUlv+7ddvcp +wFSrtXudj6AZQiXtG8ZPgqA5DtaxeRjJn6bJxJpt55XK02ln944N6ZdAzWGx/MHq +7I6ZwNXIh0sDSijBWBNUli1z/gMXTQ4+qEjM1bqtxGkZK0NFCw4G5UAr7wKBgEzZ +J295jWgf7xQ6/DBnRBiI6dRaOtTSrzFqYvdGJVktir9WB7GbuiEAJs1QxmWdYmw5 +3Jh/2MGgHbEB74uQfXKR/ptmGKZtxyX0taaJBLPjffY/CJOOB+cDEPQkbaC5BETI +c2DNChOvf8E7WiSFM8sfGhHgT/oJivszy6B4eabBAoGAL0hU0IKSvXZTllsPAEjp +N4pQFQra4QwT2W9qulzmzHpwERwWV1R72JswC+K7NU1QQmNicDp7AyfDMHUKNoOO +n5qUadLNEDhHA/2ZhJTMUZ9O+xK7IF9cTodX7d7XP+e4iAtvVRVcWHd8IslPdPCP +bjCclOuLd5K79D/nVXLM0t8= +-----END PRIVATE KEY----- diff --git a/elixir/test/support/fixtures/test.public.pem b/elixir/test/support/fixtures/test.public.pem new file mode 100644 index 0000000..8eb09a1 --- /dev/null +++ b/elixir/test/support/fixtures/test.public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqAPFN1cindknExLWAgXH +YnTpu2SaGgDz5dMI0YXmRAUMqQicwdY5h/pbnFJ1bXrR3jocyob3ndyD/0ZfhFqx +hGNYJV0ThLgTfoprW23V+hJ9nbF2FO/IyPnpq8dDMvee8NuePvbMN7A1M0mcWHFE +SdbgdHVBZ0wmh7URvkcNHTR9uYQ8FwtnhehxTK0nfvlVfHnc4xooQmA2fsKfGbWF +Bx0iWXw+XETlyTjKEiAbraMTXsLddb0Jxj5HNgmRgL5oQErs1TFxNgr0bcpy4u7r +xUnU7RRlK316WDN7bBD6s+YxB4xKbJUiEuQfF32LJlzOz/7eOoUR2M1CBe9lULx5 +nwIDAQAB +-----END PUBLIC KEY-----