Skip to content

Commit

Permalink
feat(logic): handle encoding option
Browse files Browse the repository at this point in the history
  • Loading branch information
bdeneux authored and ccamel committed Oct 27, 2023
1 parent 268e493 commit d83b5d3
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 42 deletions.
3 changes: 3 additions & 0 deletions x/logic/predicate/atom.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ var AtomEmptyArray = engine.NewAtom("[]")
// AtomNull is the term null.
var AtomNull = engine.NewAtom("null")

// AtomEncoding is the term used to indicate the encoding type option.
var AtomEncoding = engine.NewAtom("encoding")

// MakeNull returns the compound term @(null).
// It is used to represent the null value in json objects.
func MakeNull() engine.Term {
Expand Down
12 changes: 6 additions & 6 deletions x/logic/predicate/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,17 @@ const (

func ECDSAVerify(vm *engine.VM, key, data, sig, options engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return engine.Delay(func(ctx context.Context) *engine.Promise {
pubKey, err := TermToBytes(key, env)
pubKey, err := TermToBytes(key, AtomEncoding.Apply(engine.NewAtom("octet")), env)
if err != nil {
return engine.Error(fmt.Errorf("ecdsa_verify/4: decoding public key: %w", err))
}

msg, err := TermToBytes(data, env)
msg, err := TermToBytes(data, options, env)
if err != nil {
return engine.Error(fmt.Errorf("ecdsa_verify/4: decoding data: %w", err))
}

signature, err := TermToBytes(sig, env)
signature, err := TermToBytes(sig, AtomEncoding.Apply(engine.NewAtom("octet")), env)
if err != nil {
return engine.Error(fmt.Errorf("ecdsa_verify/4: decoding signature: %w", err))
}
Expand All @@ -140,17 +140,17 @@ func ECDSAVerify(vm *engine.VM, key, data, sig, options engine.Term, cont engine

func SecpVerify(vm *engine.VM, key, data, sig, options engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return engine.Delay(func(ctx context.Context) *engine.Promise {
pubKey, err := TermToBytes(key, env)
pubKey, err := TermToBytes(key, AtomEncoding.Apply(engine.NewAtom("octet")), env)
if err != nil {
return engine.Error(fmt.Errorf("secp_verify/4: decoding public key: %w", err))
}

msg, err := TermToBytes(data, env)
msg, err := TermToBytes(data, options, env)
if err != nil {
return engine.Error(fmt.Errorf("secp_verify/4: decoding data: %w", err))
}

signature, err := TermToBytes(sig, env)
signature, err := TermToBytes(sig, AtomEncoding.Apply(engine.NewAtom("octet")), env)
if err != nil {
return engine.Error(fmt.Errorf("secp_verify/4: decoding signature: %w", err))
}
Expand Down
67 changes: 38 additions & 29 deletions x/logic/predicate/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,78 +186,87 @@ func TestEDDSAVerify(t *testing.T) {
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, Msg, Sig, _).`,
ecdsa_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantResult: []types.TermResults{{}},
wantSuccess: true,
},
{ // All good with hex encoding
program: `verify :-
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, '9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Sig, encoding(hex)).`,
query: `verify.`,
wantResult: []types.TermResults{{}},
wantSuccess: true,
},
{ // Wrong Msg
program: `verify :-
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9e', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, Msg, Sig, _).`,
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9e', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantSuccess: false,
},
{ // Wrong public key
program: `verify :-
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5b5b', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, Msg, Sig, _).`,
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5b5b', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
ecdsa_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantSuccess: false,
wantError: fmt.Errorf("ecdsa_verify/4: failed verify signature: ed25519: bad public key length: 33"),
},
{ // Wrong signature
program: `verify :-
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff', Sig),
ecdsa_verify(PubKey, Msg, Sig, _).`,
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff', Sig),
ecdsa_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantSuccess: false,
},
{
// All good
program: `verify :-
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3be', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e47', Sig),
secp_verify(PubKey, Msg, Sig, _).`,
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3be', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e47', Sig),
secp_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantResult: []types.TermResults{{}},
wantSuccess: true,
},
{ // Invalid secp signature
program: `verify :-
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
secp_verify(PubKey, Msg, Sig, _).`,
hex_bytes('53167ac3fc4b720daa45b04fc73fe752578fa23a10048422d6904b7f4f7bba5a', PubKey),
hex_bytes('9b038f8ef6918cbb56040dfda401b56bb1ce79c472e7736e8677758c83367a9d', Msg),
hex_bytes('889bcfd331e8e43b5ebf430301dffb6ac9e2fce69f6227b43552fe3dc8cc1ee00c1cc53452a8712e9d5f80086dff8cf4999c1b93ed6c6e403c09334cb61ddd0b', Sig),
secp_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantSuccess: false,
wantError: fmt.Errorf("secp_verify/4: failed verify signature: failed decode PEM public key"),
},
{
// Wrong msg
program: `verify :-
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3bf', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e47', Sig),
secp_verify(PubKey, Msg, Sig, _).`,
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3bf', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e47', Sig),
secp_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantResult: []types.TermResults{{}},
wantSuccess: false,
},
{
// Wrong signature
program: `verify :-
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3be', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e48', Sig),
secp_verify(PubKey, Msg, Sig, _).`,
hex_bytes('2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414545386843612b527835565547393835506666565870433478446643660d0a6b75747a4c4b4d49586e6c383735734543525036654b4b79704c70514564564752526b3551396f687a64766b49392b583850756d666766356d673d3d0d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d', PubKey),
hex_bytes('e50c26e89f734b2ee12041ff27874c901891f74a0f0cf470333312a3034ce3be', Msg),
hex_bytes('30450220099e6f9dd218e0e304efa7a4224b0058a8e3aec73367ec239bee4ed8ed7d85db022100b504d3d0d2e879b04705c0e5a2b40b0521a5ab647ea207bd81134e1a4eb79e48', Sig),
secp_verify(PubKey, Msg, Sig, encoding(octet)).`,
query: `verify.`,
wantResult: []types.TermResults{{}},
wantSuccess: false,
Expand Down
79 changes: 72 additions & 7 deletions x/logic/predicate/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package predicate

import (
"encoding/hex"
"fmt"
"sort"

Expand Down Expand Up @@ -64,17 +65,81 @@ func BytesToList(bt []byte) engine.Term {
return engine.List(terms...)
}

func TermToBytes(term engine.Term, env *engine.Env) ([]byte, error) {
switch b := env.Resolve(term).(type) {
func OptionsContains(atom engine.Atom, options engine.Term, env *engine.Env) (engine.Compound, error) {
switch opts := env.Resolve(options).(type) {
case engine.Compound:
if b.Arity() != 2 || b.Functor().String() != "." {
return nil, fmt.Errorf("term should be a List, give %T", b)
if opts.Functor() == atom {
return opts, nil
} else if opts.Arity() == 2 && opts.Functor().String() == "." {

iter := engine.ListIterator{List: opts, Env: env}

for iter.Next() {
opt := env.Resolve(iter.Current())
term, err := OptionsContains(atom, opt, env)
if err != nil {
return nil, err
}
if term != nil {
return term, nil
}
}
}
iter := engine.ListIterator{List: b, Env: env}
return nil, nil
case nil:
return nil, nil
default:
return nil, fmt.Errorf("invalid options term, should be compound, give %T", opts)
}
}

// TermToBytes try to convert a term to native golang []byte.
// By default, if no encoding options is given the term is considered as hexadecimal value.
// Available encoding option is `text`, `octet` and `hex` (default value)
func TermToBytes(term, options engine.Term, env *engine.Env) ([]byte, error) {
encoding, err := OptionsContains(AtomEncoding, options, env)
if err != nil {
return nil, err
}

if encoding == nil {
encoding = env.Resolve(AtomEncoding.Apply(engine.NewAtom("hex"))).(engine.Compound)
}

if encoding.Arity() != 1 {
return nil, fmt.Errorf("invalid arity for encoding option, should be 1")
}

return ListToBytes(iter, env)
switch enc := env.Resolve(encoding.Arg(0)).(type) {
case engine.Atom:
switch enc.String() {
case "octet":
switch b := env.Resolve(term).(type) {
case engine.Compound:
if b.Arity() != 2 || b.Functor().String() != "." {
return nil, fmt.Errorf("term should be a List, give %T", b)
}
iter := engine.ListIterator{List: b, Env: env}

return ListToBytes(iter, env)
default:
return nil, fmt.Errorf("invalid term type: %T, should be a List", term)
}
case "hex":
switch b := env.Resolve(term).(type) {
case engine.Atom:
src := []byte(b.String())
result := make([]byte, hex.DecodedLen(len(src)))
_, err := hex.Decode(result, src)
return result, err
default:
return nil, fmt.Errorf("invalid term type: %T, should be String", term)
}
default:
return nil, fmt.Errorf("invalid encoding option: %s, valid value are 'hex' or 'octet'", enc.String())
}
default:
return nil, fmt.Errorf("invalid term type: %T, should be Atom or List", term)
return nil, fmt.Errorf("invalid given options")
}
}

Expand Down

0 comments on commit d83b5d3

Please sign in to comment.