Skip to content

Commit

Permalink
Merge f875ba7 into f2ba1b1
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Wiedemann authored May 23, 2024
2 parents f2ba1b1 + f875ba7 commit 5ddc4e3
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 27 deletions.
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type Verifier struct {
// * `baseContext`: validates that only the fields and values (when applicable)are present in the document. No extra fields are allowed (outside of credentialSubject).
// Default is set to `none` to ensure backwards compatibility
ValidationMode string `mapstructure:"validationMode" default:"none"`
// algorithm to be used for the jwt signatures - currently supported: RS256 and ES256
KeyAlgorithm string `mapstructure:"keyAlgorithm" default:"RS256"`
}

type Policies struct {
Expand Down
2 changes: 2 additions & 0 deletions config/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func Test_ReadConfig(t *testing.T) {
},
},
ValidationMode: "none",
KeyAlgorithm: "RS256",
},
Logging: Logging{
Level: "DEBUG",
Expand Down Expand Up @@ -85,6 +86,7 @@ func Test_ReadConfig(t *testing.T) {
TirAddress: "",
SessionExpiry: 30,
ValidationMode: "none",
KeyAlgorithm: "RS256",
},
Logging: Logging{
Level: "INFO",
Expand Down
40 changes: 35 additions & 5 deletions verifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"errors"
"fmt"
Expand Down Expand Up @@ -34,6 +35,7 @@ import (

var ErrorNoDID = errors.New("no_did_configured")
var ErrorNoTIR = errors.New("no_tir_configured")
var ErrorUnsupportedKeyAlgorithm = errors.New("unsupported_key_algorithm")
var ErrorUnsupportedValidationMode = errors.New("unsupported_validation_mode")
var ErrorInvalidVC = errors.New("invalid_vc")
var ErrorNoSuchSession = errors.New("no_such_session")
Expand Down Expand Up @@ -87,6 +89,8 @@ type CredentialVerifier struct {
credentialsConfig CredentialsConfig
// Validation services to be used on the credentials
validationServices []ValidationService
// Algorithm to be used for signing the jwt
signingAlgorithm string
}

// allow singleton access to the verifier
Expand Down Expand Up @@ -232,7 +236,7 @@ func InitVerifier(config *configModel.Configuration) (err error) {
trustedParticipantVerificationService := TrustedParticipantValidationService{tirClient: tirClient}
trustedIssuerVerificationService := TrustedIssuerValidationService{tirClient: tirClient}

key, err := initPrivateKey()
key, err := initPrivateKey(verifierConfig.KeyAlgorithm)

if err != nil {
logging.Log().Errorf("Was not able to initiate a signing key. Err: %v", err)
Expand All @@ -256,6 +260,7 @@ func InitVerifier(config *configModel.Configuration) (err error) {
&trustedParticipantVerificationService,
&trustedIssuerVerificationService,
},
verifierConfig.KeyAlgorithm,
}

logging.Log().Debug("Successfully initalized the verifier")
Expand Down Expand Up @@ -328,7 +333,17 @@ func (v *CredentialVerifier) GetToken(authorizationCode string, redirectUri stri
logging.Log().Infof("Redirect uri does not match for authorization %s. Was %s but is expected %s.", authorizationCode, redirectUri, tokenSession.redirect_uri)
return jwtString, expiration, ErrorRedirectUriMismatch
}
jwtBytes, err := v.tokenSigner.Sign(tokenSession.token, jwa.ES256, v.signingKey)

var signatureAlgorithm jwa.SignatureAlgorithm

switch v.signingAlgorithm {
case "RS256":
signatureAlgorithm = jwa.RS256
case "ES256":
signatureAlgorithm = jwa.ES256
}

jwtBytes, err := v.tokenSigner.Sign(tokenSession.token, signatureAlgorithm, v.signingKey)
if err != nil {
logging.Log().Warnf("Was not able to sign the token. Err: %v", err)
return jwtString, expiration, err
Expand Down Expand Up @@ -398,7 +413,15 @@ func (v *CredentialVerifier) GenerateToken(clientId, subject, audience string, s
}
expiration := token.Expiration().Unix() - v.clock.Now().Unix()

tokenBytes, err := v.tokenSigner.Sign(token, jwa.ES256, v.signingKey)
var signatureAlgorithm jwa.SignatureAlgorithm
switch v.signingAlgorithm {
case "RS256":
signatureAlgorithm = jwa.RS256
case "ES256":
signatureAlgorithm = jwa.ES256
}

tokenBytes, err := v.tokenSigner.Sign(token, signatureAlgorithm, v.signingKey)
if err != nil {
logging.Log().Warnf("Was not able to sign the token. Err: %v", err)
return 0, "", err
Expand Down Expand Up @@ -700,8 +723,15 @@ func getHostName(urlString string) (host string, err error) {
}

// Initialize the private key of the verifier. Might need to be persisted in future iterations.
func initPrivateKey() (key jwk.Key, err error) {
newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
func initPrivateKey(keyType string) (key jwk.Key, err error) {
var newKey interface{}
if keyType == "RS256" {
newKey, err = rsa.GenerateKey(rand.Reader, 2048)
} else if keyType == "ES256" {
newKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
} else {
return key, ErrorUnsupportedKeyAlgorithm
}

if err != nil {
return nil, err
Expand Down
60 changes: 38 additions & 22 deletions verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"net/http"
"net/url"
Expand Down Expand Up @@ -36,11 +37,11 @@ func TestVerifyConfig(t *testing.T) {
}

tests := []test{
{"If all mandatory parameters are present, verfication should succeed.", configModel.Verifier{Did: "did:key:verifier", TirAddress: "http:tir.de", ValidationMode: "none"}, nil},
{"If no TIR is configured, the verification should fail.", configModel.Verifier{Did: "did:key:verifier", ValidationMode: "none"}, ErrorNoTIR},
{"If no DID is configured, the verification should fail.", configModel.Verifier{TirAddress: "http:tir.de", ValidationMode: "none"}, ErrorNoDID},
{"If no DID and TIR is configured, the verification should fail.", configModel.Verifier{ValidationMode: "none"}, ErrorNoDID},
{"If no validation mode is configured, verfication should fail.", configModel.Verifier{Did: "did:key:verifier", TirAddress: "http:tir.de"}, ErrorUnsupportedValidationMode},
{"If all mandatory parameters are present, verfication should succeed.", configModel.Verifier{Did: "did:key:verifier", TirAddress: "http:tir.de", ValidationMode: "none", KeyAlgorithm: "RS256"}, nil},
{"If no TIR is configured, the verification should fail.", configModel.Verifier{Did: "did:key:verifier", ValidationMode: "none", KeyAlgorithm: "RS256"}, ErrorNoTIR},
{"If no DID is configured, the verification should fail.", configModel.Verifier{TirAddress: "http:tir.de", ValidationMode: "none", KeyAlgorithm: "RS256"}, ErrorNoDID},
{"If no DID and TIR is configured, the verification should fail.", configModel.Verifier{ValidationMode: "none", KeyAlgorithm: "RS256"}, ErrorNoDID},
{"If no validation mode is configured, verfication should fail.", configModel.Verifier{Did: "did:key:verifier", TirAddress: "http:tir.de", KeyAlgorithm: "RS256"}, ErrorUnsupportedValidationMode},
}

for _, tc := range tests {
Expand Down Expand Up @@ -468,10 +469,11 @@ func TestInitVerifier(t *testing.T) {
}

tests := []test{
{"A verifier should be properly intantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", TirAddress: "https://tir.org", ValidationMode: "none", SessionExpiry: 30}}, nil},
{"Without a did, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{TirAddress: "https://tir.org", ValidationMode: "none", SessionExpiry: 30}}, ErrorNoDID},
{"Without a tir, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", SessionExpiry: 30, ValidationMode: "none"}}, ErrorNoTIR},
{"Without a validationMode, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", TirAddress: "https://tir.org", ValidationMode: "blub", SessionExpiry: 30}}, ErrorUnsupportedValidationMode},
{"A verifier should be properly intantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", TirAddress: "https://tir.org", ValidationMode: "none", SessionExpiry: 30, KeyAlgorithm: "RS256"}}, nil},
{"Without a did, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{TirAddress: "https://tir.org", ValidationMode: "none", SessionExpiry: 30, KeyAlgorithm: "RS256"}}, ErrorNoDID},
{"Without a tir, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", SessionExpiry: 30, ValidationMode: "none", KeyAlgorithm: "RS256"}}, ErrorNoTIR},
{"Without a validationMode, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", TirAddress: "https://tir.org", ValidationMode: "blub", SessionExpiry: 30, KeyAlgorithm: "RS256"}}, ErrorUnsupportedValidationMode},
{"Without a valid key algorithm, no verifier should be instantiated.", configModel.Configuration{Verifier: configModel.Verifier{Did: "did:key:verifier", TirAddress: "https://tir.org", ValidationMode: "none", SessionExpiry: 30, KeyAlgorithm: "SomethingWeird"}}, ErrorUnsupportedKeyAlgorithm},
}

for _, tc := range tests {
Expand Down Expand Up @@ -501,21 +503,35 @@ func TestInitVerifier(t *testing.T) {
func TestGetJWKS(t *testing.T) {
logging.Configure(true, "DEBUG", true, []string{})

ecdsKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
testKey, _ := jwk.New(ecdsKey)
type test struct {
testName string
key interface{}
}
rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048)
ecdsaKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

verifier := CredentialVerifier{signingKey: testKey}
tests := []test{
{"The rsa key should have been successfully returned", rsaKey},
{"The ec key should have been successfully returned", ecdsaKey},
}

jwks := verifier.GetJWKS()
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
testKey, _ := jwk.New(tc.key)
verifier := CredentialVerifier{signingKey: testKey}

if jwks.Len() != 1 {
t.Errorf("TestGetJWKS: Exactly the current signing key should be included.")
}
returnedKey, _ := jwks.Get(0)
expectedKey, _ := testKey.PublicKey()
// we compare the json-output to avoid address comparison instead of by-value.
if logging.PrettyPrintObject(expectedKey) != logging.PrettyPrintObject(returnedKey) {
t.Errorf("TestGetJWKS: Exactly the public key should be returned. Expected %v but was %v.", logging.PrettyPrintObject(expectedKey), logging.PrettyPrintObject(returnedKey))
jwks := verifier.GetJWKS()

if jwks.Len() != 1 {
t.Errorf("TestGetJWKS: Exactly the current signing key should be included.")
}
returnedKey, _ := jwks.Get(0)
expectedKey, _ := testKey.PublicKey()
// we compare the json-output to avoid address comparison instead of by-value.
if logging.PrettyPrintObject(expectedKey) != logging.PrettyPrintObject(returnedKey) {
t.Errorf("TestGetJWKS: Exactly the public key should be returned. Expected %v but was %v.", logging.PrettyPrintObject(expectedKey), logging.PrettyPrintObject(returnedKey))
}
})
}
}

Expand Down Expand Up @@ -570,7 +586,7 @@ func TestGetToken(t *testing.T) {
logging.Log().Info("TestGetToken +++++++++++++++++ Running test: ", tc.testName)

tokenCache := mockTokenCache{tokens: tc.tokenSession}
verifier := CredentialVerifier{tokenCache: &tokenCache, signingKey: testKey, clock: mockClock{}, tokenSigner: mockTokenSigner{tc.signingError}}
verifier := CredentialVerifier{tokenCache: &tokenCache, signingKey: testKey, clock: mockClock{}, tokenSigner: mockTokenSigner{tc.signingError}, signingAlgorithm: "ES256"}
jwtString, expiration, err := verifier.GetToken(tc.testCode, tc.testRedirectUri)

if err != tc.expectedError {
Expand Down

0 comments on commit 5ddc4e3

Please sign in to comment.