Skip to content
Snippets Groups Projects
Commit 7bfdf095 authored by Lyuben Penkovski's avatar Lyuben Penkovski
Browse files

Rego functions for creating and verifying VC proofs through the signer

parent c1c4e18f
No related branches found
No related tags found
1 merge request!34Extension functions for creating and verifying VC/VP proofs
......@@ -84,16 +84,17 @@ func main() {
cacheFuncs := regofunc.NewCacheFuncs(cfg.Cache.Addr, httpClient)
didResolverFuncs := regofunc.NewDIDResolverFuncs(cfg.DIDResolver.Addr, httpClient)
taskFuncs := regofunc.NewTaskFuncs(cfg.Task.Addr, httpClient)
keysFuncs := regofunc.NewPubkeyFuncs(cfg.Signer.Addr, httpClient)
signerFuncs := regofunc.NewSignerFuncs(cfg.Signer.Addr, httpClient)
regofunc.Register("cacheGet", rego.Function3(cacheFuncs.CacheGetFunc()))
regofunc.Register("cacheSet", rego.Function4(cacheFuncs.CacheSetFunc()))
regofunc.Register("didResolve", rego.Function1(didResolverFuncs.ResolveFunc()))
regofunc.Register("taskCreate", rego.Function2(taskFuncs.CreateTaskFunc()))
regofunc.Register("taskListCreate", rego.Function2(taskFuncs.CreateTaskListFunc()))
regofunc.Register("getKey", rego.Function1(keysFuncs.GetKeyFunc()))
regofunc.Register("getAllKeys", rego.FunctionDyn(keysFuncs.GetAllKeysFunc()))
regofunc.Register("getAllKeys", rego.FunctionDyn(keysFuncs.GetAllKeysFunc()))
regofunc.Register("issuer", rego.FunctionDyn(keysFuncs.IssuerDID()))
regofunc.Register("getKey", rego.Function1(signerFuncs.GetKeyFunc()))
regofunc.Register("getAllKeys", rego.FunctionDyn(signerFuncs.GetAllKeysFunc()))
regofunc.Register("issuer", rego.FunctionDyn(signerFuncs.IssuerDID()))
regofunc.Register("createProof", rego.Function1(signerFuncs.CreateProof()))
regofunc.Register("verifyProof", rego.Function1(signerFuncs.VerifyProof()))
}
// subscribe the cache for policy data changes
......
package regofunc
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
......@@ -11,19 +13,19 @@ import (
"github.com/open-policy-agent/opa/types"
)
type PubkeyFuncs struct {
type SignerFuncs struct {
signerAddr string
httpClient *http.Client
}
func NewPubkeyFuncs(signerAddr string, httpClient *http.Client) *PubkeyFuncs {
return &PubkeyFuncs{
func NewSignerFuncs(signerAddr string, httpClient *http.Client) *SignerFuncs {
return &SignerFuncs{
signerAddr: signerAddr,
httpClient: httpClient,
}
}
func (pf *PubkeyFuncs) GetKeyFunc() (*rego.Function, rego.Builtin1) {
func (sf *SignerFuncs) GetKeyFunc() (*rego.Function, rego.Builtin1) {
return &rego.Function{
Name: "keys.get",
Decl: types.NewFunction(types.Args(types.S), types.A),
......@@ -39,7 +41,7 @@ func (pf *PubkeyFuncs) GetKeyFunc() (*rego.Function, rego.Builtin1) {
return nil, fmt.Errorf("empty keyname")
}
uri, err := url.ParseRequestURI(pf.signerAddr + "/v1/keys/" + key)
uri, err := url.ParseRequestURI(sf.signerAddr + "/v1/keys/" + key)
if err != nil {
return nil, err
}
......@@ -49,7 +51,7 @@ func (pf *PubkeyFuncs) GetKeyFunc() (*rego.Function, rego.Builtin1) {
return nil, err
}
resp, err := pf.httpClient.Do(req.WithContext(bctx.Context))
resp, err := sf.httpClient.Do(req.WithContext(bctx.Context))
if err != nil {
return nil, err
}
......@@ -68,14 +70,14 @@ func (pf *PubkeyFuncs) GetKeyFunc() (*rego.Function, rego.Builtin1) {
}
}
func (pf *PubkeyFuncs) GetAllKeysFunc() (*rego.Function, rego.BuiltinDyn) {
func (sf *SignerFuncs) GetAllKeysFunc() (*rego.Function, rego.BuiltinDyn) {
return &rego.Function{
Name: "keys.getAll",
Decl: types.NewFunction(nil, types.A),
Memoize: true,
},
func(bctx rego.BuiltinContext, terms []*ast.Term) (*ast.Term, error) {
uri, err := url.ParseRequestURI(pf.signerAddr + "/v1/keys")
uri, err := url.ParseRequestURI(sf.signerAddr + "/v1/keys")
if err != nil {
return nil, err
}
......@@ -85,7 +87,7 @@ func (pf *PubkeyFuncs) GetAllKeysFunc() (*rego.Function, rego.BuiltinDyn) {
return nil, err
}
resp, err := pf.httpClient.Do(req.WithContext(bctx.Context))
resp, err := sf.httpClient.Do(req.WithContext(bctx.Context))
if err != nil {
return nil, err
}
......@@ -104,14 +106,14 @@ func (pf *PubkeyFuncs) GetAllKeysFunc() (*rego.Function, rego.BuiltinDyn) {
}
}
func (pf *PubkeyFuncs) IssuerDID() (*rego.Function, rego.BuiltinDyn) {
func (sf *SignerFuncs) IssuerDID() (*rego.Function, rego.BuiltinDyn) {
return &rego.Function{
Name: "issuer",
Decl: types.NewFunction(nil, types.A),
Memoize: true,
},
func(bctx rego.BuiltinContext, terms []*ast.Term) (*ast.Term, error) {
uri, err := url.ParseRequestURI(pf.signerAddr + "/v1/issuerDID")
uri, err := url.ParseRequestURI(sf.signerAddr + "/v1/issuerDID")
if err != nil {
return nil, err
}
......@@ -121,7 +123,7 @@ func (pf *PubkeyFuncs) IssuerDID() (*rego.Function, rego.BuiltinDyn) {
return nil, err
}
resp, err := pf.httpClient.Do(req.WithContext(bctx.Context))
resp, err := sf.httpClient.Do(req.WithContext(bctx.Context))
if err != nil {
return nil, err
}
......@@ -139,3 +141,135 @@ func (pf *PubkeyFuncs) IssuerDID() (*rego.Function, rego.BuiltinDyn) {
return ast.NewTerm(v), nil
}
}
func (sf *SignerFuncs) CreateProof() (*rego.Function, rego.Builtin1) {
return &rego.Function{
Name: "proof.create",
Decl: types.NewFunction(types.Args(types.S), types.A),
Memoize: true,
},
func(bctx rego.BuiltinContext, credential *ast.Term) (*ast.Term, error) {
// cred represents verifiable credential or presentation
var cred map[string]interface{}
if err := ast.As(credential.Value, &cred); err != nil {
return nil, fmt.Errorf("invalid credential: %s", err)
}
if cred["type"] == nil {
return nil, fmt.Errorf("credential data does not specify type: must be VerifiablePresentation or VerifiableCredential")
}
credType, ok := cred["type"].(string)
if !ok {
return nil, fmt.Errorf("invalid credential type, string is expected")
}
var createProofPath string
switch credType {
case "VerifiableCredential":
createProofPath = "/v1/credential/proof"
case "VerifiablePresentation":
createProofPath = "/v1/presentation/proof"
default:
return nil, fmt.Errorf("unknown credential type: %q", credType)
}
jsonCred, err := json.Marshal(cred)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", sf.signerAddr+createProofPath, bytes.NewReader(jsonCred))
if err != nil {
return nil, err
}
resp, err := sf.httpClient.Do(req.WithContext(bctx.Context))
if err != nil {
return nil, err
}
defer resp.Body.Close() // nolint:errcheck
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected response from signer: %d", resp.StatusCode)
}
v, err := ast.ValueFromReader(resp.Body)
if err != nil {
return nil, err
}
return ast.NewTerm(v), nil
}
}
func (sf *SignerFuncs) VerifyProof() (*rego.Function, rego.Builtin1) {
return &rego.Function{
Name: "proof.verify",
Decl: types.NewFunction(types.Args(types.S), types.A),
Memoize: true,
},
func(bctx rego.BuiltinContext, credential *ast.Term) (*ast.Term, error) {
// cred represents verifiable credential or presentation
var cred map[string]interface{}
if err := ast.As(credential.Value, &cred); err != nil {
return nil, fmt.Errorf("invalid credential: %s", err)
}
if cred["type"] == nil {
return nil, fmt.Errorf("credential data does not specify type: must be VerifiablePresentation or VerifiableCredential")
}
credType, ok := cred["type"].(string)
if !ok {
return nil, fmt.Errorf("invalid credential type, string is expected")
}
if cred["proof"] == nil {
return nil, fmt.Errorf("credential data does contain proof section")
}
var verifyProofPath string
switch credType {
case "VerifiableCredential":
verifyProofPath = "/v1/credential/verify"
case "VerifiablePresentation":
verifyProofPath = "/v1/presentation/verify"
default:
return nil, fmt.Errorf("unknown credential type: %q", credType)
}
jsonCred, err := json.Marshal(cred)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", sf.signerAddr+verifyProofPath, bytes.NewReader(jsonCred))
if err != nil {
return nil, err
}
resp, err := sf.httpClient.Do(req.WithContext(bctx.Context))
if err != nil {
return nil, err
}
defer resp.Body.Close() // nolint:errcheck
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected response from signer: %d", resp.StatusCode)
}
var result struct {
Valid bool `json:"valid"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode response from signer: %v", err)
}
if !result.Valid {
return nil, fmt.Errorf("proof is invalid")
}
return ast.NewTerm(ast.Boolean(true)), nil
}
}
......@@ -21,7 +21,7 @@ func TestGetKeyFunc(t *testing.T) {
}))
defer signerSrv.Close()
keysFuncs := regofunc.NewPubkeyFuncs(signerSrv.URL, http.DefaultClient)
keysFuncs := regofunc.NewSignerFuncs(signerSrv.URL, http.DefaultClient)
r := rego.New(
rego.Query(`keys.get("key1")`),
rego.Function1(keysFuncs.GetKeyFunc()),
......@@ -41,7 +41,7 @@ func TestGetKeyFuncError(t *testing.T) {
}))
defer signerSrv.Close()
keysFuncs := regofunc.NewPubkeyFuncs(signerSrv.URL, http.DefaultClient)
keysFuncs := regofunc.NewSignerFuncs(signerSrv.URL, http.DefaultClient)
r := rego.New(
rego.Query(`keys.get("key1")`),
rego.Function1(keysFuncs.GetKeyFunc()),
......@@ -62,7 +62,7 @@ func TestGetAllKeysFunc(t *testing.T) {
}))
defer signerSrv.Close()
keysFuncs := regofunc.NewPubkeyFuncs(signerSrv.URL, http.DefaultClient)
keysFuncs := regofunc.NewSignerFuncs(signerSrv.URL, http.DefaultClient)
r := rego.New(
rego.Query(`keys.getAll()`),
rego.FunctionDyn(keysFuncs.GetAllKeysFunc()),
......@@ -83,7 +83,7 @@ func TestIssuerDID(t *testing.T) {
}))
defer signerSrv.Close()
keysFuncs := regofunc.NewPubkeyFuncs(signerSrv.URL, http.DefaultClient)
keysFuncs := regofunc.NewSignerFuncs(signerSrv.URL, http.DefaultClient)
r := rego.New(
rego.Query(`issuer()`),
rego.FunctionDyn(keysFuncs.IssuerDID()),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment