Compare commits

...

19 Commits

Author SHA1 Message Date
glyph 757a498378 Merge pull request 'Add encoder(s)' (#1) from encoder into main
Reviewed-on: #1
2022-09-21 11:37:27 +00:00
glyph b2f7328f78 add license and docs about encoding support 2022-09-21 12:35:18 +01:00
glyph bac29888bd add encoding of boolean, nil, list, map and tuple 2022-09-21 12:12:14 +01:00
glyph e31466b128 add encoder for nil values 2022-09-21 12:11:20 +01:00
glyph f72b8f153f add tests for encoding boolean, nil, list, map and tuple 2022-09-21 12:10:30 +01:00
glyph 1da93e3260 add happy path test for plain string 2022-09-20 19:48:21 +01:00
glyph 22c820ca73 remove low-level tests and add api tests for string encoding 2022-09-20 19:35:52 +01:00
glyph 3732f34ded add lockfile to ignore list 2022-09-20 19:34:41 +01:00
glyph e4b9dc7b54 add string encoding 2022-09-20 19:33:58 +01:00
glyph 99b6a046b4 add test for str encoding 2022-09-20 11:38:01 +01:00
glyph 58d87d9c6a add str encoder 2022-09-20 11:37:24 +01:00
glyph dc4009978f add test for sig decoding 2022-09-20 11:26:00 +01:00
glyph 8ce0502782 add sig encoder and improve base64 extractor 2022-09-20 11:25:27 +01:00
glyph 40a108f5e5 formatting 2022-09-20 11:24:31 +01:00
glyph 53a3369d62 add tests for classic feed and message encoding 2022-09-19 17:59:42 +01:00
glyph 9c75595755 add encoders for classic feed and message types 2022-09-19 17:58:58 +01:00
mycognosist 056d34104f update manifest 2022-09-19 16:39:48 +01:00
mycognosist 8535fa1fa2 add basic tests for types plus blob, bool and box encoding 2022-09-19 16:39:11 +01:00
mycognosist 2c4869c294 add encoders for blob, bool and box 2022-09-19 16:38:04 +01:00
7 changed files with 234 additions and 6 deletions

3
.gitignore vendored
View File

@ -24,3 +24,6 @@ ssb_bfe-*.tar
# Temporary files, for example, from tests.
/tmp/
# Ignore the lockfile
mix.lock

View File

@ -2,6 +2,41 @@
Binary Field Encodings (BFE) for Secure Scuttlebutt (SSB).
See the [SSB Binary Field Encodings Specification](https://github.com/ssbc/ssb-bfe-spec) for details.
## Encoding
```elixir
SsbBfe.encode(value)
```
### Elixir Types
Encoding of the following Elixir types is supported:
- List
- Map
- Tuple
- String
- Boolean
- Nil
- Integer
- Float
Note: encoding of structs is not supported. You may wish to convert your struct to amap and encode it that way.
### Scuttlebutt Types
Encoding of the following Scuttlebutt types is supported:
- Feed (classic)
- Message (classic)
- Blob
- Signature
- Private message (box, box2)
Note: encoding of URIs is not currently supported, nor are other formats such as Gabby Grove and Bendy Butt.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
@ -15,7 +50,12 @@ def deps do
end
```
## Documentation
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/ssb_bfe>.
## License
LGPL-3.0.

View File

@ -15,4 +15,50 @@ defmodule SsbBfe do
def hello do
:world
end
def encode(value) when is_list(value) do
Enum.map(value, fn x -> encode(x) end)
end
def encode(value) when is_map(value) do
Enum.reduce(
value,
%{},
fn {k, v}, acc ->
Map.put(acc, k, SsbBfe.encode(v))
end
)
end
def encode(value) when is_tuple(value) do
Enum.map(Tuple.to_list(value), fn x -> encode(x) end)
end
def encode(value) when is_bitstring(value) do
cond do
String.starts_with?(value, "@") ->
SsbBfe.Encoder.encode_feed(value)
String.starts_with?(value, "%") ->
SsbBfe.Encoder.encode_msg(value)
String.starts_with?(value, "&") ->
SsbBfe.Encoder.encode_blob(value)
String.ends_with?(value, ".sig.ed25519") ->
SsbBfe.Encoder.encode_sig(value)
String.ends_with?(value, [".box", ".box2"]) ->
SsbBfe.Encoder.encode_box(value)
true ->
SsbBfe.Encoder.encode_str(value)
end
end
def encode(value) when is_boolean(value), do: SsbBfe.Encoder.encode_bool(value)
def encode(value) when is_number(value), do: value
def encode(value) when is_nil(value), do: SsbBfe.Encoder.encode_nil()
end

View File

@ -1,5 +1,55 @@
# might be good to use a multiclause function for the `encode` function
# - use the `when` conditional guard with e.g. `is_number`
defmodule SsbBfe.Encoder do
def encode_bool(blob_id) do
# Enum.sum(list) / Kernel.length(list)
# Extract the base64 substring from a sigil-link and decode it.
defp extract_base64_data(str) do
base64_data = String.slice(str, 1..44)
Base.decode64(base64_data)
end
end
# Provide a pattern with which to split a string and base64 decode the first part.
defp extract_base64_data(str, pattern) do
[base64_data | _] = String.split(str, pattern)
Base.decode64(base64_data)
end
def encode_blob(blob_id) do
blob_tf_tag = SsbBfe.Types.get_blob_type(blob_id)
{:ok, decoded_base64_data} = extract_base64_data(blob_id)
blob_tf_tag <> decoded_base64_data
end
def encode_bool(true), do: <<6, 1, 1>>
def encode_bool(false), do: <<6, 1, 0>>
def encode_box(box_str) do
box_tf_tag = SsbBfe.Types.get_box_type(box_str)
{:ok, decoded_base64_data} = extract_base64_data(box_str, ".")
box_tf_tag <> decoded_base64_data
end
def encode_feed(feed_id) do
feed_tf_tag = SsbBfe.Types.get_feed_type(feed_id)
{:ok, decoded_base64_data} = extract_base64_data(feed_id)
feed_tf_tag <> decoded_base64_data
end
def encode_msg(msg_id) do
msg_tf_tag = SsbBfe.Types.get_msg_type(msg_id)
{:ok, decoded_base64_data} = extract_base64_data(msg_id)
msg_tf_tag <> decoded_base64_data
end
def encode_nil(), do: <<6, 2>>
def encode_sig(sig) do
{:ok, decoded_base64_data} = extract_base64_data(sig, ".sig.ed25519")
<<4, 0>> <> decoded_base64_data
end
def encode_str(str), do: <<6, 0>> <> str
# def encode_uri(uri)
end

View File

@ -8,7 +8,7 @@ defmodule SsbBfe.Types do
<<2, 0>>
end
end
@doc ~S"""
Take a box as a string and return the encoded bytes representing the box
type-format. Return `nil` if the ID does not end with `.box` or `.box2`.
@ -17,8 +17,10 @@ defmodule SsbBfe.Types do
cond do
String.ends_with?(boxed_str, ".box") ->
<<5, 0>>
String.ends_with?(boxed_str, ".box2") ->
<<5, 1>>
true ->
nil
end
@ -43,8 +45,10 @@ defmodule SsbBfe.Types do
cond do
String.ends_with?(msg_id, ".sha256") ->
<<1, 0>>
String.ends_with?(msg_id, ".cloaked") ->
<<1, 2>>
true ->
nil
end

View File

@ -23,6 +23,7 @@ defmodule SsbBfe.MixProject do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
{:poison, "~> 5.0"}
]
end
end

View File

@ -2,7 +2,91 @@ defmodule SsbBfeTest do
use ExUnit.Case
doctest SsbBfe
test "greets the world" do
assert SsbBfe.hello() == :world
setup do
[
blob: "&S7+CwHM6dZ9si5Vn4ftpk/l/ldbRMqzzJos+spZbWf4=.sha256",
box1: "bG92ZSBjb2xsYXBzZXMgc3BhY2V0aW1l.box",
box2: "bG92ZSBjb2xsYXBzZXMgc3BhY2V0aW1l.box2",
feed_classic: "@d/zDvFswFbQaYJc03i47C9CgDev+/A8QQSfG5l/SEfw=.ed25519",
msg_classic: "%R8heq/tQoxEIPkWf0Kxn1nCm/CsxG2CDpUYnAvdbXY8=.sha256",
sig: "nkY4Wsn9feosxvX7bpLK7OxjdSrw6gSL8sun1n2TMLXKySYK9L5itVQnV2nQUctFsrUOa2istD2vDk1B0uAMBQ==.sig.ed25519",
str: "golden ripples in the meshwork",
]
end
# HAPPY PATH ENCODING TESTS
test "classic feed is encoded correctly", context do
encoded_feed = SsbBfe.encode(context.feed_classic)
assert encoded_feed == <<0, 0, 119, 252, 195, 188, 91, 48, 21, 180, 26, 96, 151, 52, 222, 46, 59, 11, 208, 160, 13, 235, 254, 252, 15, 16, 65, 39, 198, 230, 95, 210, 17, 252>>
end
test "classic msg is encoded correctly", context do
encoded_msg = SsbBfe.encode(context.msg_classic)
assert encoded_msg == <<1, 0, 71, 200, 94, 171, 251, 80, 163, 17, 8, 62, 69, 159, 208, 172, 103, 214, 112, 166, 252, 43, 49, 27, 96, 131, 165, 70, 39, 2, 247, 91, 93, 143>>
end
test "blob is encoded correctly", context do
encoded_blob = SsbBfe.encode(context.blob)
assert encoded_blob == <<2, 0, 75, 191, 130, 192, 115, 58, 117, 159, 108, 139, 149, 103, 225, 251, 105, 147, 249, 127, 149, 214, 209, 50, 172, 243, 38, 139, 62, 178, 150, 91, 89, 254>>
end
test "signature is encoded correctly", context do
encoded_sig = SsbBfe.encode(context.sig)
assert encoded_sig == <<4, 0, 158, 70, 56, 90, 201, 253, 125, 234, 44, 198, 245, 251, 110, 146, 202, 236, 236, 99, 117, 42, 240, 234, 4, 139, 242, 203, 167, 214, 125, 147, 48, 181, 202, 201, 38, 10, 244, 190, 98, 181, 84, 39, 87, 105, 208, 81, 203, 69, 178, 181, 14, 107, 104, 172, 180, 61, 175, 14, 77, 65, 210, 224, 12, 5>>
end
test "box is encoded correctly", context do
encoded_box = SsbBfe.encode(context.box1)
assert encoded_box == <<5, 0, 108, 111, 118, 101, 32, 99, 111, 108, 108, 97, 112, 115, 101, 115, 32, 115, 112, 97, 99, 101, 116, 105, 109, 101>>
end
test "box2 is encoded correctly", context do
encoded_box2 = SsbBfe.encode(context.box2)
assert encoded_box2 == <<5, 1, 108, 111, 118, 101, 32, 99, 111, 108, 108, 97, 112, 115, 101, 115, 32, 115, 112, 97, 99, 101, 116, 105, 109, 101>>
end
test "plain string is encoded correctly", context do
encoded_str = SsbBfe.encode(context.str)
assert encoded_str == <<6, 0, 103, 111, 108, 100, 101, 110, 32, 114, 105, 112, 112, 108, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 109, 101, 115, 104, 119, 111, 114, 107>>
end
test "boolean is encoded correctly" do
encoded_bool_true = SsbBfe.encode(true)
encoded_bool_false = SsbBfe.encode(false)
assert encoded_bool_true == <<6, 1, 1>>
assert encoded_bool_false == <<6, 1, 0>>
end
test "nil is encoded correctly" do
encoded_nil = SsbBfe.encode(nil)
assert encoded_nil == <<6, 2>>
end
test "list is encoded correctly" do
encoded_list = SsbBfe.encode([true, nil, "ganoderma"])
assert encoded_list == [<<6, 1, 1>>, <<6, 2>>, <<6, 0, 103, 97, 110, 111, 100, 101, 114, 109, 97>>]
end
test "map is encoded correctly", context do
encoded_map = SsbBfe.encode(%{"bool" => false, "feed" => context.feed_classic})
assert encoded_map == %{"bool" => <<6, 1, 0>>, "feed" => <<0, 0, 119, 252, 195, 188, 91, 48, 21, 180, 26, 96, 151, 52, 222, 46, 59, 11, 208, 160, 13, 235, 254, 252, 15, 16, 65, 39, 198, 230, 95, 210, 17, 252>>}
end
test "tuple is encoded correctly", context do
encoded_tuple = SsbBfe.encode({7, context.msg_classic})
assert encoded_tuple == [7, <<1, 0, 71, 200, 94, 171, 251, 80, 163, 17, 8, 62, 69, 159, 208, 172, 103, 214, 112, 166, 252, 43, 49, 27, 96, 131, 165, 70, 39, 2, 247, 91, 93, 143>>]
end
end