Skip to content

Commit

Permalink
ParseAcceptProfile
Browse files Browse the repository at this point in the history
  • Loading branch information
volodymyr-basiuk committed Dec 20, 2024
1 parent 71b2ecc commit 685c211
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packers/anoncrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ func (p *AnoncryptPacker) MediaType() iden3comm.MediaType {
func (p *AnoncryptPacker) GetSupportedProfiles() []string {
return []string{
fmt.Sprintf(
"%s;env=%s",
"%s;env=%s&alg=%s",
protocol.ProtocolVersionV1,
p.MediaType(),
jose.ECDH_ES_A256KW,
),
}
}
22 changes: 22 additions & 0 deletions protocol/accept.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
package protocol

import (
"github.com/iden3/iden3comm/v2"
)

// AcceptProfile is a struct that represents the accept header
type AcceptProfile struct {
ProtocolVersion AcceptProtocolVersion
Env iden3comm.MediaType
Circuits []AcceptAuthCircuits
AcceptJwzAlgorithms []AcceptJwzAlgorithms
AcceptJwsAlgorithms []AcceptJwsAlgorithms
AcceptAnoncryptAlgorithms []AcceptAnoncryptAlgorithms
}

// AcceptProtocolVersion is a type of supported versions of the protocol used in the accept header
type AcceptProtocolVersion string

Expand Down Expand Up @@ -36,3 +50,11 @@ const (
// AcceptJwsAlgorithmsES256KR is a ES256K-R accepted JWS algorithm
AcceptJwsAlgorithmsES256KR AcceptJwsAlgorithms = "ES256K-R"
)

// AcceptAnoncryptAlgorithms is a type of accepted anoncrypt algorithms
type AcceptAnoncryptAlgorithms string

const (
// AcceptAnoncryptECDHESA256KW is a ECDH-ES+A256KW accepted Anoncrypt algorithm
AcceptAnoncryptECDHESA256KW AcceptAnoncryptAlgorithms = "ECDH-ES+A256KW"
)
1 change: 1 addition & 0 deletions protocol/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type AuthorizationRequestMessageBody struct {
Message string `json:"message,omitempty"`
DIDDoc json.RawMessage `json:"did_doc,omitempty"`
Scope []ZeroKnowledgeProofRequest `json:"scope"`
Accept []string `json:"accept,omitempty"`
}

// ZeroKnowledgeProofRequest represents structure of zkp request object
Expand Down
192 changes: 192 additions & 0 deletions utils/accept.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package utils

import (
"errors"
"strings"

"github.com/iden3/iden3comm/v2"
"github.com/iden3/iden3comm/v2/packers"
"github.com/iden3/iden3comm/v2/protocol"
)

// ParseAcceptProfile parses the accept profile string and returns the AcceptProfile struct
func ParseAcceptProfile(profile string) (protocol.AcceptProfile, error) {
params := strings.Split(profile, ";")
if len(params) < 2 {
return protocol.AcceptProfile{}, errors.New("invalid accept profile value")
}

protocolVersion := strings.TrimSpace(params[0])
if !isProtocolVersion(protocolVersion) {
return protocol.AcceptProfile{}, errors.New("protocol version '" + protocolVersion + "' not supported")
}

envParam := strings.Split(params[1], "=")
if len(envParam) != 2 {
return protocol.AcceptProfile{}, errors.New("invalid accept profile 'env' parameter")
}

env := strings.TrimSpace(envParam[1])
if !isMediaType(env) {
return protocol.AcceptProfile{}, errors.New("envelop '" + env + "' not supported")
}

circuitsIndex := -1
for i, param := range params {
if strings.Contains(param, "circuitId=") {
circuitsIndex = i
break
}
}

if env != string(packers.MediaTypeZKPMessage) && circuitsIndex > 0 {
return protocol.AcceptProfile{}, errors.New("circuits not supported for env '" + env + "'")
}

var circuits []protocol.AcceptAuthCircuits
if circuitsIndex > 0 {
circuitsStr := strings.Split(strings.Split(params[circuitsIndex], "=")[1], ",")
for _, c := range circuitsStr {
c = strings.TrimSpace(c)
if !isAcceptAuthCircuits(c) {
return protocol.AcceptProfile{}, errors.New("circuit '" + c + "' not supported")
}
circuits = append(circuits, protocol.AcceptAuthCircuits(c))
}
}

algIndex := -1
for i, param := range params {
if strings.Contains(param, "alg=") {
algIndex = i
break
}
}

var jwzAlgs []protocol.AcceptJwzAlgorithms
var jwsAlgs []protocol.AcceptJwsAlgorithms
var anoncryptAlgs []protocol.AcceptAnoncryptAlgorithms
if algIndex > 0 {
algStr := strings.Split(strings.Split(params[algIndex], "=")[1], ",")
switch env {
case string(packers.MediaTypeZKPMessage):
for _, a := range algStr {
a = strings.TrimSpace(a)
if !isAcceptJwzAlgorithms(a) {
return protocol.AcceptProfile{}, errors.New("algorithm '" + a + "' not supported for '" + env + "'")
}
jwzAlgs = append(jwzAlgs, protocol.AcceptJwzAlgorithms(a))
}
case string(packers.MediaTypeSignedMessage):
for _, a := range algStr {
a = strings.TrimSpace(a)
if !isAcceptJwsAlgorithms(a) {
return protocol.AcceptProfile{}, errors.New("algorithm '" + a + "' not supported for '" + env + "'")
}
jwsAlgs = append(jwsAlgs, protocol.AcceptJwsAlgorithms(a))
}
case string(packers.MediaTypeEncryptedMessage):
for _, a := range algStr {
a = strings.TrimSpace(a)
if !isAcceptAnoncryptAlgorithms(a) {
return protocol.AcceptProfile{}, errors.New("algorithm '" + a + "' not supported for '" + env + "'")
}
anoncryptAlgs = append(anoncryptAlgs, protocol.AcceptAnoncryptAlgorithms(a))
}
default:
return protocol.AcceptProfile{}, errors.New("algorithm not supported for '" + env + "'")
}
}

return protocol.AcceptProfile{
ProtocolVersion: protocol.AcceptProtocolVersion(protocolVersion),
Env: iden3comm.MediaType(env),
Circuits: circuits,
AcceptJwsAlgorithms: jwsAlgs,
AcceptJwzAlgorithms: jwzAlgs,
AcceptAnoncryptAlgorithms: anoncryptAlgs,
}, nil
}

func isProtocolVersion(value string) bool {
// List all possible protocol versions
validVersions := []protocol.AcceptProtocolVersion{
protocol.ProtocolVersionV1,
}
for _, v := range validVersions {
if protocol.AcceptProtocolVersion(value) == v {
return true
}
}
return false
}

func isAcceptAuthCircuits(value string) bool {
// List all possible authentication circuits
validCircuits := []protocol.AcceptAuthCircuits{
protocol.AcceptAuthCircuitsAuthV2,
protocol.AcceptAuthCircuitsAuthV3,
}
for _, v := range validCircuits {
if protocol.AcceptAuthCircuits(value) == v {
return true
}
}
return false
}

func isAcceptJwzAlgorithms(value string) bool {
// List all possible JWZ algorithms
validAlgorithms := []protocol.AcceptJwzAlgorithms{
protocol.AcceptJwzAlgorithmsGroth16,
}
for _, v := range validAlgorithms {
if protocol.AcceptJwzAlgorithms(value) == v {
return true
}
}
return false
}

func isAcceptJwsAlgorithms(value string) bool {
// List all possible JWS algorithms
validAlgorithms := []protocol.AcceptJwsAlgorithms{
protocol.AcceptJwsAlgorithmsES256K,
protocol.AcceptJwsAlgorithmsES256KR,
}
for _, v := range validAlgorithms {
if protocol.AcceptJwsAlgorithms(value) == v {
return true
}
}
return false
}

func isAcceptAnoncryptAlgorithms(value string) bool {
// List all possible Anoncrypt algorithms
validAlgorithms := []protocol.AcceptAnoncryptAlgorithms{
protocol.AcceptAnoncryptECDHESA256KW,
}
for _, v := range validAlgorithms {
if protocol.AcceptAnoncryptAlgorithms(value) == v {
return true
}
}
return false
}

func isMediaType(value string) bool {
// List all possible JWS algorithms
validAlgorithms := []iden3comm.MediaType{
packers.MediaTypeEncryptedMessage,
packers.MediaTypePlainMessage,
packers.MediaTypeZKPMessage,
packers.MediaTypeSignedMessage,
}
for _, v := range validAlgorithms {
if iden3comm.MediaType(value) == v {
return true
}
}
return false
}
124 changes: 124 additions & 0 deletions utils/accept_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package utils

import (
"errors"
"testing"

"github.com/iden3/iden3comm/v2/packers"
"github.com/iden3/iden3comm/v2/protocol"
"github.com/stretchr/testify/assert"
)

func TestAcceptProfileParser(t *testing.T) {
type expected struct {
profile protocol.AcceptProfile
err error
}
for _, tc := range []struct {
desc string
accept string
expected expected
}{
{
desc: "Valid plain text accept profile",
accept: "iden3comm/v1;env=application/iden3comm-plain-json",
expected: expected{
profile: protocol.AcceptProfile{
ProtocolVersion: protocol.ProtocolVersionV1,
Env: packers.MediaTypePlainMessage,
},
},
},
{
desc: "Valid anoncrypt accept profile",
accept: "iden3comm/v1;env=application/iden3comm-encrypted-json;alg=ECDH-ES+A256KW",
expected: expected{
profile: protocol.AcceptProfile{
ProtocolVersion: protocol.ProtocolVersionV1,
Env: packers.MediaTypeEncryptedMessage,
AcceptAnoncryptAlgorithms: []protocol.AcceptAnoncryptAlgorithms{protocol.AcceptAnoncryptECDHESA256KW},
},
},
},
{
desc: "Valid JWS accept profile",
accept: "iden3comm/v1;env=application/iden3comm-signed-json;alg=ES256K-R",
expected: expected{
profile: protocol.AcceptProfile{
ProtocolVersion: protocol.ProtocolVersionV1,
Env: packers.MediaTypeSignedMessage,
AcceptJwsAlgorithms: []protocol.AcceptJwsAlgorithms{protocol.AcceptJwsAlgorithmsES256KR},
},
},
},
{
desc: "Valid JWZ accept profile",
accept: "iden3comm/v1;env=application/iden3-zkp-json;circuitId=authV2,authV3;alg=groth16",
expected: expected{
profile: protocol.AcceptProfile{
ProtocolVersion: protocol.ProtocolVersionV1,
Env: packers.MediaTypeZKPMessage,
AcceptJwzAlgorithms: []protocol.AcceptJwzAlgorithms{protocol.AcceptJwzAlgorithmsGroth16},
Circuits: []protocol.AcceptAuthCircuits{protocol.AcceptAuthCircuitsAuthV2, protocol.AcceptAuthCircuitsAuthV3},
},
},
},
{
desc: "Invalid accept profile",
accept: "iden3comm/v1",
expected: expected{
err: errors.New("invalid accept profile value"),
},
},
{
desc: "Invalid protocol version profile",
accept: "iden3comm/v1000_000;env=application/iden3comm-plain-json",
expected: expected{
err: errors.New("protocol version 'iden3comm/v1000_000' not supported"),
},
},
{
desc: "Invalid envelop param",
accept: "iden3comm/v1;application/iden3comm-plain-json",
expected: expected{
err: errors.New("invalid accept profile 'env' parameter"),
},
},
{
desc: "Invalid envelop",
accept: "iden3comm/v1;env=application/iden3comm-rich-text",
expected: expected{
err: errors.New("envelop 'application/iden3comm-rich-text' not supported"),
},
},
{
desc: "Invalid circuit ID",
accept: "iden3comm/v1;env=application/iden3-zkp-json;circuitId=authV2.5;alg=groth16",
expected: expected{
err: errors.New("circuit 'authV2.5' not supported"),
},
},
{
desc: "Invalid alg",
accept: "iden3comm/v1;env=application/iden3-zkp-json;circuitId=authV2;alg=groth1",
expected: expected{
err: errors.New("algorithm 'groth1' not supported for 'application/iden3-zkp-json'"),
},
},
{
desc: "Alg for plain message",
accept: "iden3comm/v1;env=application/iden3comm-plain-json;alg=someAlg",
expected: expected{
err: errors.New("algorithm not supported for 'application/iden3comm-plain-json'"),
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
profile, err := ParseAcceptProfile(tc.accept)
if tc.expected.err != nil {
assert.Equal(t, err.Error(), tc.expected.err.Error())
}
assert.Equal(t, profile, tc.expected.profile)
})
}
}

0 comments on commit 685c211

Please sign in to comment.