Skip to content

Commit

Permalink
Merge pull request #1800 from bstasyszyn/bitstring-statuslist-encoding-2
Browse files Browse the repository at this point in the history
fix: Change encoding for BitstringStatusList encodedList to multibase
  • Loading branch information
bstasyszyn authored Nov 22, 2024
2 parents c0362a2 + 5b70ee2 commit 94e2ea6
Show file tree
Hide file tree
Showing 20 changed files with 597 additions and 124 deletions.
12 changes: 6 additions & 6 deletions component/credentialstatus/credentialstatus_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,19 @@ import (

"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/multiformats/go-multibase"
"github.com/piprate/json-gold/ld"
"github.com/stretchr/testify/require"
"github.com/trustbloc/kms-go/spi/kms"
longform "github.com/trustbloc/sidetree-go/pkg/vdr/sidetreelongform"

"github.com/trustbloc/vcs/internal/mock/vcskms"

timeutil "github.com/trustbloc/did-go/doc/util/time"
vdr2 "github.com/trustbloc/did-go/vdr"
vdr "github.com/trustbloc/did-go/vdr/api"
vdrmock "github.com/trustbloc/did-go/vdr/mock"
"github.com/trustbloc/kms-go/spi/kms"
longform "github.com/trustbloc/sidetree-go/pkg/vdr/sidetreelongform"
"github.com/trustbloc/vc-go/verifiable"

"github.com/trustbloc/vcs/component/credentialstatus/internal/testutil"
"github.com/trustbloc/vcs/internal/mock/vcskms"
"github.com/trustbloc/vcs/pkg/cslmanager"
"github.com/trustbloc/vcs/pkg/doc/vc"
"github.com/trustbloc/vcs/pkg/doc/vc/bitstring"
Expand Down Expand Up @@ -121,7 +120,8 @@ func validateBitstringStatusListEntry(
require.Equal(t, statustype.StatusListBitstringVCSubjectType, credSubject[0].CustomFields["type"].(string))
require.Equal(t, "revocation", credSubject[0].CustomFields[statustype.StatusPurpose].(string))
require.NotEmpty(t, credSubject[0].CustomFields["encodedList"].(string))
bitString, err := bitstring.DecodeBits(credSubject[0].CustomFields["encodedList"].(string))
bitString, err := bitstring.DecodeBits(credSubject[0].CustomFields["encodedList"].(string),
bitstring.WithMultibaseEncoding(multibase.Base64url))
require.NoError(t, err)

revocationListIndex, err := strconv.Atoi(statusID.TypedID.CustomFields[statustype.StatusListIndex].(string))
Expand Down
2 changes: 1 addition & 1 deletion component/credentialstatus/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ toolchain go1.22.4
require (
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0
github.com/multiformats/go-multibase v0.2.0
github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
Expand Down Expand Up @@ -94,7 +95,6 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multihash v0.0.14 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/jinzhu/copier v0.3.5
github.com/klauspost/compress v1.17.9
github.com/labstack/echo/v4 v4.12.0
github.com/multiformats/go-multibase v0.2.0
github.com/ory/dockertest/v3 v3.10.1-0.20240704115616-d229e74b748d
github.com/ory/fosite v0.47.0
github.com/ory/x v0.0.655
Expand Down Expand Up @@ -179,7 +180,6 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
Expand Down
78 changes: 68 additions & 10 deletions pkg/doc/vc/bitstring/bitstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"compress/gzip"
"encoding/base64"
"fmt"

"github.com/multiformats/go-multibase"
)

const (
Expand All @@ -20,21 +22,70 @@ const (

// BitString struct.
type BitString struct {
bits []byte
numBits int
bits []byte
numBits int
multibaseEncoding multibase.Encoding
}

type Opt func(*options)

type options struct {
multibaseEncoding multibase.Encoding
}

// WithMultibaseEncoding sets the multibase encoding.
func WithMultibaseEncoding(value multibase.Encoding) Opt {
return func(options *options) {
options.multibaseEncoding = value
}
}

// NewBitString return bitstring.
func NewBitString(length int) *BitString {
func NewBitString(length int, opts ...Opt) *BitString {
options := &options{}

for _, opt := range opts {
opt(options)
}

size := 1 + ((length - 1) / bitsPerByte)
return &BitString{bits: make([]byte, size), numBits: length}

return &BitString{
bits: make([]byte, size),
numBits: length,
multibaseEncoding: options.multibaseEncoding,
}
}

// DecodeBits decode bits.
func DecodeBits(encodedBits string) (*BitString, error) {
decodedBits, err := base64.RawURLEncoding.DecodeString(encodedBits)
if err != nil {
return nil, err
func DecodeBits(encodedBits string, opts ...Opt) (*BitString, error) {
options := &options{}

for _, opt := range opts {
opt(options)
}

var decodedBits []byte

if options.multibaseEncoding != multibase.Encoding(0) {
var encoding multibase.Encoding
var err error

encoding, decodedBits, err = multibase.Decode(encodedBits)
if err != nil {
return nil, err
}

if encoding != options.multibaseEncoding {
return nil, fmt.Errorf("encoding not supported: %d", encoding)
}
} else {
var err error

decodedBits, err = base64.RawURLEncoding.DecodeString(encodedBits)
if err != nil {
return nil, err
}
}

b := bytes.NewReader(decodedBits)
Expand All @@ -49,7 +100,10 @@ func DecodeBits(encodedBits string) (*BitString, error) {
return nil, err
}

return &BitString{bits: buf.Bytes()}, nil
return &BitString{
bits: buf.Bytes(),
multibaseEncoding: options.multibaseEncoding,
}, nil
}

// Set bit.
Expand Down Expand Up @@ -99,5 +153,9 @@ func (b *BitString) EncodeBits() (string, error) {
return "", err
}

return base64.RawURLEncoding.EncodeToString(buf.Bytes()), nil
if b.multibaseEncoding == multibase.Encoding(0) {
return base64.RawURLEncoding.EncodeToString(buf.Bytes()), nil
}

return multibase.Encode(b.multibaseEncoding, buf.Bytes())
}
52 changes: 52 additions & 0 deletions pkg/doc/vc/bitstring/bitstring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package bitstring
import (
"testing"

"github.com/multiformats/go-multibase"
"github.com/stretchr/testify/require"
)

Expand All @@ -31,6 +32,21 @@ func TestBitString(t *testing.T) {
require.Contains(t, err.Error(), "illegal base64 data at input")
})

t.Run("invalid multibase string", func(t *testing.T) {
_, err := DecodeBits("!!!!wrongvalue", WithMultibaseEncoding(multibase.Base64url))
require.Error(t, err)
require.Contains(t, err.Error(), "selected encoding not supported")
})

t.Run("unsupported multibase encoding", func(t *testing.T) {
str, err := multibase.Encode(multibase.Base64pad, []byte("data"))
require.NoError(t, err)

_, err = DecodeBits(str, WithMultibaseEncoding(multibase.Base64url))
require.Error(t, err)
require.Contains(t, err.Error(), "encoding not supported")
})

t.Run("test success", func(t *testing.T) {
bitString := NewBitString(17)

Expand Down Expand Up @@ -66,4 +82,40 @@ func TestBitString(t *testing.T) {
require.NoError(t, err)
require.False(t, bitSet)
})

t.Run("Multibase-encoding success", func(t *testing.T) {
bitString := NewBitString(17, WithMultibaseEncoding(multibase.Base64url))

err := bitString.Set(1, true)
require.NoError(t, err)

bitSet, err := bitString.Get(1)
require.NoError(t, err)
require.True(t, bitSet)

bitSet, err = bitString.Get(0)
require.NoError(t, err)
require.False(t, bitSet)

encodeBits, err := bitString.EncodeBits()
require.NoError(t, err)

bitStr, err := DecodeBits(encodeBits, WithMultibaseEncoding(multibase.Base64url))
require.NoError(t, err)

bitSet, err = bitStr.Get(1)
require.NoError(t, err)
require.True(t, bitSet)

bitSet, err = bitStr.Get(0)
require.NoError(t, err)
require.False(t, bitSet)

err = bitStr.Set(1, false)
require.NoError(t, err)

bitSet, err = bitStr.Get(1)
require.NoError(t, err)
require.False(t, bitSet)
})
}
2 changes: 2 additions & 0 deletions pkg/doc/vc/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type StatusProcessor interface {
CreateVC(vcID string, listSize int, profile *Signer) (*verifiable.Credential, error)
CreateVCStatus(index, vcID, purpose string, additionalFields ...Field) *verifiable.TypedID
GetVCContext() string
UpdateStatus(vc *verifiable.Credential, status bool, indexes ...int) (*verifiable.Credential, error)
IsSet(vc *verifiable.Credential, index int) (bool, error)
}

type StatusProcessorGetter func(vcStatusListType StatusType) (StatusProcessor, error)
10 changes: 8 additions & 2 deletions pkg/doc/vc/statustype/revocationlist2020.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ const (

// revocationList2020Processor implements Revocation List 2020.
// Spec: https://w3c-ccg.github.io/vc-status-rl-2020/
type revocationList2020Processor struct{}
type revocationList2020Processor struct {
*statusListProcessor
}

// NewRevocationList2020Processor returns new revocationList2020Processor.
func NewRevocationList2020Processor() *revocationList2020Processor { //nolint:revive
return &revocationList2020Processor{}
return &revocationList2020Processor{
statusListProcessor: &statusListProcessor{
statusType: revocationList2020VCSubjectType,
},
}
}

// GetStatusVCURI returns the ID (URL) of status VC.
Expand Down
10 changes: 8 additions & 2 deletions pkg/doc/vc/statustype/revocationlist2021.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ const (

// revocationList2021Processor implements version 0.0.1 of Status list 2021.
// Release: https://github.com/w3c-ccg/vc-status-list-2021/releases/tag/v0.0.1
type revocationList2021Processor struct{}
type revocationList2021Processor struct {
*statusListProcessor
}

// NewRevocationList2021Processor returns new revocationList2021Processor.
func NewRevocationList2021Processor() *revocationList2021Processor { //nolint:revive
return &revocationList2021Processor{}
return &revocationList2021Processor{
statusListProcessor: &statusListProcessor{
statusType: revocationList2021VCSubjectType,
},
}
}

// GetStatusVCURI returns the ID (URL) of status VC.
Expand Down
10 changes: 8 additions & 2 deletions pkg/doc/vc/statustype/statuslist2021.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ const (

// statusList2021Processor implements f Status List 2021.
// Spec: https://w3c-ccg.github.io/vc-status-list-2021/#statuslist2021credential
type statusList2021Processor struct{}
type statusList2021Processor struct {
*statusListProcessor
}

// NewStatusList2021Processor returns new statusList2021Processor.
func NewStatusList2021Processor() *statusList2021Processor { //nolint:revive
return &statusList2021Processor{}
return &statusList2021Processor{
statusListProcessor: &statusListProcessor{
statusType: StatusList2021VCSubjectType,
},
}
}

// GetStatusVCURI returns the ID (URL) of status VC.
Expand Down
14 changes: 11 additions & 3 deletions pkg/doc/vc/statustype/statuslist_bitstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

"github.com/google/uuid"
"github.com/multiformats/go-multibase"
utiltime "github.com/trustbloc/did-go/doc/util/time"
"github.com/trustbloc/vc-go/verifiable"

Expand All @@ -38,11 +39,18 @@ const (

// BitstringStatusListProcessor implements the Bitstring Status List Entry.
// Spec: https://www.w3.org/TR/vc-bitstring-status-list/
type BitstringStatusListProcessor struct{}
type BitstringStatusListProcessor struct {
*statusListProcessor
}

// NewBitstringStatusListProcessor returns new BitstringStatusListProcessor.
func NewBitstringStatusListProcessor() *BitstringStatusListProcessor {
return &BitstringStatusListProcessor{}
return &BitstringStatusListProcessor{
statusListProcessor: &statusListProcessor{
statusType: StatusListBitstringVCSubjectType,
multibaseEncoding: multibase.Base64url,
},
}
}

// GetStatusVCURI returns the ID (URL) of status VC.
Expand Down Expand Up @@ -177,7 +185,7 @@ func (s *BitstringStatusListProcessor) CreateVC(vcID string, listSize int,
size = bitStringSize
}

encodeBits, err := bitstring.NewBitString(size).EncodeBits()
encodeBits, err := bitstring.NewBitString(size, bitstring.WithMultibaseEncoding(multibase.Base64url)).EncodeBits()
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 94e2ea6

Please sign in to comment.