Skip to content

Commit

Permalink
Merge branch 'changes-today'
Browse files Browse the repository at this point in the history
* changes-today:
  Update mockgen source paths in makefile
  Remove unused interfaces and simplify parser usage
  Refactor: remove PemReader interface and simplify usage
  Remove DidCreator and DidParser interfaces
  Reverse chain order in the x5c header.
  Move FixChainHeaders from x509_test_utils.go to x509_cert.go

# Conflicts:
#	main.go
#	makefile
#	uzi_vc_issuer/ura_issuer_test.go
  • Loading branch information
rolandgroen committed Oct 14, 2024
2 parents 8ead512 + cc81693 commit 6ed82f4
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 151 deletions.
26 changes: 2 additions & 24 deletions did_x509/did_x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,6 @@ type X509Did struct {
SanType x509_cert.SanTypeName
}

// DidCreator is an interface for creating a DID (Decentralized Identifier) given a chain of x509 certificates.
// The CreateDid method takes a slice of x509.Certificate and returns a DID as a string and an error if any.
type DidCreator interface {
CreateDid(chain *[]x509.Certificate) (string, error)
}

type DidParser interface {
ParseDid(did string) (*X509Did, error)
}

// DefaultDidProcessor is responsible for creating Decentralized Identifiers (DIDs) based on certificate chain information.
type DefaultDidProcessor struct {
}

// NewDidCreator initializes and returns a new instance of DefaultDidProcessor.
func NewDidCreator() *DefaultDidProcessor {
return &DefaultDidProcessor{}
}
func NewDidParser() *DefaultDidProcessor {
return &DefaultDidProcessor{}
}

// FormatDid constructs a decentralized identifier (DID) from a certificate chain and an optional policy.
// It returns the formatted DID string or an error if the root certificate or hash calculation fails.
func FormatDid(chain *[]x509.Certificate, policy string) (string, error) {
Expand All @@ -64,7 +42,7 @@ func FormatDid(chain *[]x509.Certificate, policy string) (string, error) {
// CreateDid generates a Decentralized Identifier (DID) from a given certificate chain.
// It extracts the Unique Registration Address (URA) from the chain, creates a policy with it, and formats the DID.
// Returns the generated DID or an error if any step fails.
func (d *DefaultDidProcessor) CreateDid(chain *[]x509.Certificate) (string, error) {
func CreateDid(chain *[]x509.Certificate) (string, error) {
certificate, _, err := x509_cert.FindSigningCertificate(chain)
if err != nil || certificate == nil {
return "", err
Expand All @@ -77,7 +55,7 @@ func (d *DefaultDidProcessor) CreateDid(chain *[]x509.Certificate) (string, erro
formattedDid, err := FormatDid(chain, policy)
return formattedDid, err
}
func (d *DefaultDidProcessor) ParseDid(didString string) (*X509Did, error) {
func ParseDid(didString string) (*X509Did, error) {
x509Did := X509Did{}
didObj := did.MustParseDID(didString)
if didObj.Method != "x509" {
Expand Down
69 changes: 65 additions & 4 deletions did_x509/did_x509_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestDefaultDidCreator_CreateDid(t *testing.T) {
type args struct {
chain *[]x509.Certificate
}
chain, _, rootCert, _, _, err := x509_cert.BuildCertChain("A BIG STRING")
chain, _, rootCert, _, _, err := x509_cert.BuildCertChain("A_BIG_STRING")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -54,14 +54,13 @@ func TestDefaultDidCreator_CreateDid(t *testing.T) {
name: "Happy path",
fields: fields{},
args: args{chain: chain},
want: strings.Join([]string{"did", "x509", "0", alg, rootHashString, "", "san", "otherName", "A BIG STRING"}, ":"),
want: strings.Join([]string{"did", "x509", "0", alg, rootHashString, "", "san", "otherName", "A_BIG_STRING"}, ":"),
errMsg: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &DefaultDidProcessor{}
got, err := d.CreateDid(tt.args.chain)
got, err := CreateDid(tt.args.chain)
wantErr := tt.errMsg != ""
if (err != nil) != wantErr {
t.Errorf("DefaultDidProcessor.CreateDid() error = %v, errMsg %v", err, tt.errMsg)
Expand All @@ -78,3 +77,65 @@ func TestDefaultDidCreator_CreateDid(t *testing.T) {
})
}
}

// TestDefaultDidCreator_ParseDid tests the ParseDid function of DefaultDidProcessor by providing different DID strings.
// It checks for correct X509Did parsing and appropriate error messages.
func TestDefaultDidCreator_ParseDid(t *testing.T) {
type fields struct {
}
type args struct {
didString string
}
tests := []struct {
name string
fields fields
args args
want *X509Did
errMsg string
}{
{
name: "Invalid DID method",
fields: fields{},
args: args{didString: "did:abc:0:sha512:hash::san:otherName:A_BIG_STRING"},
want: nil,
errMsg: "invalid didString method",
},
{
name: "Invalid DID format",
fields: fields{},
args: args{didString: "did:x509:0:sha512::san:otherName:A_BIG_STRING"},
want: nil,
errMsg: "invalid didString format, expected didString:x509:0:alg:hash::san:type:ura",
},
{
name: "Happy path",
fields: fields{},
args: args{didString: "did:x509:0:sha512:hash::san:otherName:A_BIG_STRING"},
want: &X509Did{Version: "0", RootCertificateHashAlg: "sha512", RootCertificateHash: "hash", SanType: "otherName", Ura: "A_BIG_STRING"},
errMsg: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseDid(tt.args.didString)
wantErr := tt.errMsg != ""
if (err != nil) != wantErr {
t.Errorf("DefaultDidProcessor.ParseDid() error = %v, expected error = %v", err, tt.errMsg)
return
} else if wantErr {
if err.Error() != tt.errMsg {
t.Errorf("DefaultDidProcessor.ParseDid() expected = \"%v\", got = \"%v\"", tt.errMsg, err.Error())
}
}

if tt.want != nil && got != nil &&
(tt.want.Version != got.Version ||
tt.want.RootCertificateHashAlg != got.RootCertificateHashAlg ||
tt.want.RootCertificateHash != got.RootCertificateHash ||
tt.want.SanType != got.SanType ||
tt.want.Ura != got.Ura) {
t.Errorf("DefaultDidProcessor.ParseDid() = %v, want = %v", got, tt.want)
}
})
}
}
7 changes: 1 addition & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package main
import (
"fmt"
"github.com/alecthomas/kong"
"github.com/nuts-foundation/uzi-did-x509-issuer/did_x509"
"github.com/nuts-foundation/uzi-did-x509-issuer/uzi_vc_issuer"
"github.com/nuts-foundation/uzi-did-x509-issuer/x509_cert"
"os"
)

Expand Down Expand Up @@ -41,8 +39,5 @@ func main() {
}

func issueVc(vc VC) (string, error) {
didCreator := did_x509.NewDidCreator()
chainParser := x509_cert.NewDefaultChainParser()
issuer := uzi_vc_issuer.NewUraVcBuilder(didCreator, chainParser)
return issuer.Issue(vc.CertificateFile, vc.SigningKey, vc.SubjectDID, vc.Test)
return uzi_vc_issuer.Issue(vc.CertificateFile, vc.SigningKey, vc.SubjectDID, vc.Test)
}
3 changes: 0 additions & 3 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ install-tools:
go install github.com/golangci/golangci-lint/cmd/[email protected]

gen-mocks:
mockgen -destination=did_x509/did_x509_mock.go -package=did_x509 -source=did_x509/did_x509.go
mockgen -destination=pem/pem_reader_mock.go -package=pem -source=pem/pem_reader.go
mockgen -destination=x509_cert/x509_cert_mock.go -package=x509_cert -source=x509_cert/x509_cert.go

lint:
golangci-lint run -v
Expand Down
16 changes: 1 addition & 15 deletions pem/pem_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,8 @@ import (
"os"
)

// PemReader defines the interface for parsing PEM-encoded files from a given path.
type PemReader interface {
ParseFileOrPath(path string, pemType string) (*[][]byte, error)
}

// DefaultPemReader handles reading and parsing of PEM files or directories containing PEM files.
type DefaultPemReader struct {
}

// NewPemReader creates and returns a new instance of DefaultPemReader.
func NewPemReader() *DefaultPemReader {
return &DefaultPemReader{}
}

// ParseFileOrPath processes a file or directory at the given path and extracts PEM blocks of the specified pemType.
func (p *DefaultPemReader) ParseFileOrPath(path string, pemType string) (*[][]byte, error) {
func ParseFileOrPath(path string, pemType string) (*[][]byte, error) {
fileInfo, err := os.Stat(path)
if err != nil {
return nil, err
Expand Down
18 changes: 6 additions & 12 deletions pem/pem_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ func TestParseFileOrPath(t *testing.T) {
pemType := "CERTIFICATE"

t.Run("FileExistsAndIsNotDirectory", func(t *testing.T) {
pemReader := NewPemReader()
result, err := pemReader.ParseFileOrPath(tempFile.Name(), pemType)
result, err := ParseFileOrPath(tempFile.Name(), pemType)
assert.NoError(t, err)
assert.NotNil(t, result)
})

t.Run("FileDoesNotExist", func(t *testing.T) {
pemReader := NewPemReader()
_, err := pemReader.ParseFileOrPath("nonexistent", pemType)
_, err := ParseFileOrPath("nonexistent", pemType)
assert.Error(t, err)
})

Expand All @@ -42,18 +40,15 @@ func TestParseFileOrPath(t *testing.T) {
}(tempDir)

t.Run("PathIsDirectory", func(t *testing.T) {
pemReader := NewPemReader()
_, err := pemReader.ParseFileOrPath(tempDir, pemType)
_, err := ParseFileOrPath(tempDir, pemType)
assert.NoError(t, err)
})

t.Run("PathDoesNotExist", func(t *testing.T) {
pemReader := NewPemReader()
_, err := pemReader.ParseFileOrPath("nonexistent/path", pemType)
_, err := ParseFileOrPath("nonexistent/path", pemType)
assert.Error(t, err)
})
t.Run("Happy flow single file", func(t *testing.T) {
pemReader := NewPemReader()
file, err := os.CreateTemp(tempDir, "prefix")
if err != nil {
t.Fatal(err)
Expand All @@ -78,7 +73,7 @@ func TestParseFileOrPath(t *testing.T) {
t.Fail()
}
}
data, err := pemReader.ParseFileOrPath(file.Name(), pemType)
data, err := ParseFileOrPath(file.Name(), pemType)
assert.NoError(t, err)
for i := 0; i < len(*data); i++ {
bytes := (*data)[i]
Expand All @@ -91,7 +86,6 @@ func TestParseFileOrPath(t *testing.T) {

})
t.Run("Happy flow directory", func(t *testing.T) {
pemReader := NewPemReader()
certs, chainPem, _, _, _, err := x509_cert.BuildCertChain("A BIG STRING")
assert.NoError(t, err)
tempDir, _ := os.MkdirTemp("", "example")
Expand All @@ -113,7 +107,7 @@ func TestParseFileOrPath(t *testing.T) {
t.Fail()
}
}
data, err := pemReader.ParseFileOrPath(tempDir, pemType)
data, err := ParseFileOrPath(tempDir, pemType)
assert.NoError(t, err)
dataMap := make(map[string][]byte)
for i := 0; i < len(*data); i++ {
Expand Down
51 changes: 17 additions & 34 deletions uzi_vc_issuer/ura_issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,15 @@ import (
)
import "github.com/nuts-foundation/go-did/vc"

type UraIssuer interface {

// Issue generates a digital certificate from the given certificate file and signing key file for the subject.
Issue(certificateFile string, signingKeyFile string, subjectDID string, subjectName string) (string, error)
}

var RegexOtherNameValue = regexp.MustCompile(`2\.16\.528\.1\.1007.\d+\.\d+-\d+-\d+-S-(\d+)-00\.000-\d+`)

// DefaultUraIssuer is responsible for building URA (UZI-register abonneenummer) Verifiable Credentials.
// It utilizes a DidCreator to generate Decentralized Identifiers (DIDs) given a chain of x509 certificates.
type DefaultUraIssuer struct {
didCreator did_x509.DidCreator
chainParser x509_cert.ChainParser
}

// NewUraVcBuilder initializes and returns a new instance of DefaultUraIssuer with the provided DidCreator.
func NewUraVcBuilder(didCreator did_x509.DidCreator, chainParser x509_cert.ChainParser) *DefaultUraIssuer {
return &DefaultUraIssuer{didCreator, chainParser}
}

// Issue generates a URA Verifiable Credential using provided certificate, signing key, subject DID, and subject name.
func (u DefaultUraIssuer) Issue(certificateFile string, signingKeyFile string, subjectDID string, test bool) (string, error) {
reader := pem2.NewPemReader()
certificate, err := reader.ParseFileOrPath(certificateFile, "CERTIFICATE")
func Issue(certificateFile string, signingKeyFile string, subjectDID string, test bool) (string, error) {
certificate, err := pem2.ParseFileOrPath(certificateFile, "CERTIFICATE")
if err != nil {
return "", err
}
_certificates, err := u.chainParser.ParseCertificates(certificate)
_certificates, err := x509_cert.ParseCertificates(certificate)
if err != nil {
return "", err
}
Expand All @@ -69,7 +50,7 @@ func (u DefaultUraIssuer) Issue(certificateFile string, signingKeyFile string, s
_chain := append(*chain, *certificate...)
chain = &_chain

signingKeys, err := reader.ParseFileOrPath(signingKeyFile, "PRIVATE KEY")
signingKeys, err := pem2.ParseFileOrPath(signingKeyFile, "PRIVATE KEY")
if err != nil {
return "", err
}
Expand All @@ -85,25 +66,25 @@ func (u DefaultUraIssuer) Issue(certificateFile string, signingKeyFile string, s
err := fmt.Errorf("no signing keys found")
return "", err
}
privateKey, err := u.chainParser.ParsePrivateKey(signingKey)
privateKey, err := x509_cert.ParsePrivateKey(signingKey)
if err != nil {
return "", err
}

certChain, err := u.chainParser.ParseCertificates(chain)
certChain, err := x509_cert.ParseCertificates(chain)
if err != nil {
return "", err
}

credential, err := u.BuildUraVerifiableCredential(certChain, privateKey, subjectDID)
credential, err := BuildUraVerifiableCredential(certChain, privateKey, subjectDID)
if err != nil {
return "", err
}
marshal, err := json.Marshal(credential)
if err != nil {
return "", err
}
validator := uzi_vc_validator.NewUraValidator(did_x509.NewDidParser(), test)
validator := uzi_vc_validator.NewUraValidator(test)
jwtString := string(marshal)
jwtString = jwtString[1:] // Chop start
jwtString = jwtString[:len(jwtString)-1] // Chop end
Expand All @@ -115,7 +96,7 @@ func (u DefaultUraIssuer) Issue(certificateFile string, signingKeyFile string, s
}

// BuildUraVerifiableCredential constructs a verifiable credential with specified certificates, signing key, subject DID.
func (v DefaultUraIssuer) BuildUraVerifiableCredential(certificates *[]x509.Certificate, signingKey *rsa.PrivateKey, subjectDID string) (*vc.VerifiableCredential, error) {
func BuildUraVerifiableCredential(certificates *[]x509.Certificate, signingKey *rsa.PrivateKey, subjectDID string) (*vc.VerifiableCredential, error) {
signingCert, otherNameValue, err := x509_cert.FindSigningCertificate(certificates)
if err != nil {
return nil, err
Expand All @@ -125,7 +106,7 @@ func (v DefaultUraIssuer) BuildUraVerifiableCredential(certificates *[]x509.Cert
if err != nil {
return nil, err
}
did, err := v.didCreator.CreateDid(chain)
did, err := did_x509.CreateDid(chain)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -184,15 +165,17 @@ func (v DefaultUraIssuer) BuildUraVerifiableCredential(certificates *[]x509.Cert
// marshalChain converts a slice of x509.Certificate instances to a cert.Chain, encoding each certificate as PEM.
// It returns the PEM-encoded cert.Chain and an error if the encoding or header fixation fails.
func marshalChain(certificates *[]x509.Certificate) (*cert.Chain, error) {
chainPems := &cert.Chain{}
for _, certificate := range *certificates {
err := chainPems.Add(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw}))
rv := &cert.Chain{}
certs := *certificates
for i, _ := range certs {
certificate := certs[len(certs)-i-1]
err := rv.Add(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw}))
if err != nil {
return nil, err
}
}
headers, err := x509_cert.FixChainHeaders(chainPems)
return headers, err
rv, err := x509_cert.FixChainHeaders(rv)
return rv, err
}

func validateChain(certificates *[]x509.Certificate) error {
Expand Down
Loading

0 comments on commit 6ed82f4

Please sign in to comment.