diff --git a/TODO.md b/TODO.md index 5f3e193..2251498 100644 --- a/TODO.md +++ b/TODO.md @@ -7,3 +7,9 @@ - Option to provide "additional validation" for sd-jwt validation - Option to provide "additional validation" for kb-jwt validation - Function to retrieve kb-jwt contents as map + + +Signing: + - pass json object + - specify keys to selectively disclose + - return sdjwt object diff --git a/getters.go b/getters.go new file mode 100644 index 0000000..36696bc --- /dev/null +++ b/getters.go @@ -0,0 +1,61 @@ +package go_sd_jwt + +// Body returns the body of the JWT +func (s *SdJwt) Body() *map[string]any { + return &s.body +} + +// Token returns the JWT token as it was received +func (s *SdJwt) Token() string { + return s.token +} + +// Signature returns the signature of the provided token used to verify it +func (s *SdJwt) Signature() string { + return s.signature +} + +// Head returns the head of the JWT +func (s *SdJwt) Head() map[string]any { + return s.head +} + +// Disclosures returns the disclosures of the SD-JWT +func (s *SdJwt) Disclosures() []Disclosure { + return s.disclosures +} + +// PublicKey returns the public key json (if provided) +func (s *SdJwt) PublicKey() string { + return s.publicKey +} + +// KbJwt returns the signed kb-jwt (if provided) +func (s *SdJwt) KbJwt() *string { + return s.kbJwt +} + +// ClaimName returns the claim name of the disclosure +func (d *Disclosure) ClaimName() *string { + return d.claimName +} + +// ClaimValue returns the claim value of the disclosure +func (d *Disclosure) ClaimValue() string { + return d.claimValue +} + +// Salt returns the salt of the disclosure +func (d *Disclosure) Salt() string { + return d.salt +} + +// RawValue returns the decoded contents of the disclosure +func (d *Disclosure) RawValue() string { + return d.rawValue +} + +// EncodedValue returns the disclosure as it was listed in the original SD-JWT +func (d *Disclosure) EncodedValue() string { + return d.encodedValue +} diff --git a/internal/error/error.go b/internal/error/error.go index c1f99be..eed2ac7 100644 --- a/internal/error/error.go +++ b/internal/error/error.go @@ -1,5 +1,7 @@ package error +import "errors" + type InvalidToken struct { Message string } @@ -7,3 +9,7 @@ type InvalidToken struct { func (e *InvalidToken) Error() string { return e.Message } + +var InvalidJsonError = errors.New("") +var UnknownDisclosureError = errors.New("") +var ClaimNotFoundError = errors.New("") diff --git a/internal/jose/algorithms/es256/ES256.go b/internal/jose/algorithms/es256/ES256.go index 089d52b..7da1d23 100644 --- a/internal/jose/algorithms/es256/ES256.go +++ b/internal/jose/algorithms/es256/ES256.go @@ -29,7 +29,7 @@ func (signer *ES256) ValidateSignature(token, signature string, publicKeyJson st return ecdsa.Verify(pk, bodyHash[:], r, s), nil } -func (signer *ES256) Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { +func (signer *ES256) Sign(body map[string]any, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { curve := elliptic.P256() pk, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { @@ -52,6 +52,6 @@ func (signer *ES256) Sign(body map[string]interface{}, headerKeys map[string]str return signedToken, pk, pubKey, nil } -func (signer *ES256) SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) { +func (signer *ES256) SignWithKey(body map[string]any, headerKeys map[string]string, privateKey string) (*string, error) { return nil, nil //todo } diff --git a/internal/jose/algorithms/es256/ES256_test.go b/internal/jose/algorithms/es256/ES256_test.go index 01391a0..c57a41b 100644 --- a/internal/jose/algorithms/es256/ES256_test.go +++ b/internal/jose/algorithms/es256/ES256_test.go @@ -25,7 +25,7 @@ func TestValidateSignatureES256(t *testing.T) { } func TestES256_Sign(t *testing.T) { - body := map[string]interface{}{ + body := map[string]any{ "firstname": "john", "surname": "smith", "address": map[string]string{ diff --git a/internal/jose/algorithms/es384/ES384.go b/internal/jose/algorithms/es384/ES384.go index 238c426..1d63a87 100644 --- a/internal/jose/algorithms/es384/ES384.go +++ b/internal/jose/algorithms/es384/ES384.go @@ -29,7 +29,7 @@ func (signer *ES384) ValidateSignature(token, signature string, publicKeyJson st return ecdsa.Verify(pk, bodyHash[:], r, s), nil } -func (signer *ES384) Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { +func (signer *ES384) Sign(body map[string]any, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { curve := elliptic.P384() pk, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { @@ -52,6 +52,6 @@ func (signer *ES384) Sign(body map[string]interface{}, headerKeys map[string]str return signedToken, pk, pubKey, nil } -func (signer *ES384) SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) { +func (signer *ES384) SignWithKey(body map[string]any, headerKeys map[string]string, privateKey string) (*string, error) { return nil, nil //todo } diff --git a/internal/jose/algorithms/es384/ES384_test.go b/internal/jose/algorithms/es384/ES384_test.go index c5f75e4..5385624 100644 --- a/internal/jose/algorithms/es384/ES384_test.go +++ b/internal/jose/algorithms/es384/ES384_test.go @@ -25,7 +25,7 @@ func TestValidateSignatureES384(t *testing.T) { } func TestES384_Sign(t *testing.T) { - body := map[string]interface{}{ + body := map[string]any{ "firstname": "john", "surname": "smith", "address": map[string]string{ diff --git a/internal/jose/algorithms/es512/ES512.go b/internal/jose/algorithms/es512/ES512.go index 1cf90d1..a476d74 100644 --- a/internal/jose/algorithms/es512/ES512.go +++ b/internal/jose/algorithms/es512/ES512.go @@ -29,7 +29,7 @@ func (signer *ES512) ValidateSignature(token, signature string, publicKeyJson st return ecdsa.Verify(pk, bodyHash[:], r, s), nil } -func (signer *ES512) Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { +func (signer *ES512) Sign(body map[string]any, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { curve := elliptic.P521() pk, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { @@ -52,6 +52,6 @@ func (signer *ES512) Sign(body map[string]interface{}, headerKeys map[string]str return signedToken, pk, pubKey, nil } -func (signer *ES512) SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) { +func (signer *ES512) SignWithKey(body map[string]any, headerKeys map[string]string, privateKey string) (*string, error) { return nil, nil //todo } diff --git a/internal/jose/algorithms/es512/ES512_test.go b/internal/jose/algorithms/es512/ES512_test.go index b9b7e11..ce2c814 100644 --- a/internal/jose/algorithms/es512/ES512_test.go +++ b/internal/jose/algorithms/es512/ES512_test.go @@ -25,7 +25,7 @@ func TestValidateSignatureES512(t *testing.T) { } func TestES512_Sign(t *testing.T) { - body := map[string]interface{}{ + body := map[string]any{ "firstname": "john", "surname": "smith", "address": map[string]string{ diff --git a/internal/jose/signature.go b/internal/jose/signature.go index bf11c3a..597afb2 100644 --- a/internal/jose/signature.go +++ b/internal/jose/signature.go @@ -11,8 +11,8 @@ import ( type Signer interface { ValidateSignature(token, signature string, publicKey string) (bool, error) - Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) - SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) + Sign(body map[string]any, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) + SignWithKey(body map[string]any, headerKeys map[string]string, privateKey string) (*string, error) } func GetSigner(alg string) (Signer, error) { diff --git a/sd-jwt.go b/sd-jwt.go index 7c41a2c..cad3715 100644 --- a/sd-jwt.go +++ b/sd-jwt.go @@ -15,6 +15,7 @@ import ( "github.com/MichaelFraser99/go-sd-jwt/internal/jose" "hash" "reflect" + "slices" "strings" ) @@ -313,6 +314,9 @@ func newDisclosure(d []byte) (*Disclosure, error) { if len(parts) == 2 { disclosure.setSalt(*cleanStr(parts[0])) disclosure.setClaimValue(*cleanStr(parts[1])) + } else if strings.Contains(parts[1], "{") || strings.Contains(parts[1], "[") { + disclosure.setSalt(*cleanStr(parts[0])) + disclosure.setClaimValue(*cleanStr(strings.Join(parts[1:], ","))) } else { parts[2] = strings.Join(parts[2:], ",") parts = parts[:3] @@ -325,6 +329,7 @@ func newDisclosure(d []byte) (*Disclosure, error) { disclosure.setClaimName(cleanStr(parts[1])) disclosure.setClaimValue(*cleanStr(parts[2])) } + return disclosure, nil } @@ -360,7 +365,7 @@ func validateDisclosures(disclosures []string) ([]Disclosure, error) { return disclosureArray, nil } -func validateDigests(body map[string]interface{}) error { +func validateDigests(body map[string]any) error { digests := getDigests(body) for _, d := range digests { @@ -384,70 +389,123 @@ func validateDigests(body map[string]interface{}) error { // 3. The SD-JWT contains an unsupported value for the _sd_alg claim // 4. The SD-JWT has a disclosure that is malformed for the use (e.g. doesn't contain a claim name for a non-array digest) func (s *SdJwt) GetDisclosedClaims() (map[string]any, error) { - bodyMap := make(map[string]any) disclosuresToCheck := make([]Disclosure, len(s.disclosures)) copy(disclosuresToCheck, s.disclosures) - for len(disclosuresToCheck) > 0 { - d := disclosuresToCheck[0] - - var h hash.Hash - - switch strings.ToLower(s.body["_sd_alg"].(string)) { - case "sha-256", "": - // default to sha-256 - h = sha256.New() - case "sha-224": - h = sha256.New224() - case "sha-512": - h = sha512.New() - case "sha-384": - h = sha512.New384() - case "sha-512/224": - h = sha512.New512_224() - case "sha-512/256": - h = sha512.New512_256() - case "sha3-224": - h = crypto.SHA3_224.New() - case "sha3-256": - h = crypto.SHA3_256.New() - case "sha3-384": - h = crypto.SHA3_384.New() - case "sha3-512": - h = crypto.SHA3_512.New() - default: - return nil, errors.New("unsupported _sd_alg: " + s.body["_sd_alg"].(string)) - } - h.Write([]byte(d.EncodedValue())) - hashedDisclosures := h.Sum(nil) - base64HashedDisclosureBytes := make([]byte, base64.RawURLEncoding.EncodedLen(len(hashedDisclosures))) - base64.RawURLEncoding.Encode(base64HashedDisclosureBytes, hashedDisclosures) + var h hash.Hash + + switch strings.ToLower(s.body["_sd_alg"].(string)) { + case "sha-256", "": + // default to sha-256 + h = sha256.New() + case "sha-224": + h = sha256.New224() + case "sha-512": + h = sha512.New() + case "sha-384": + h = sha512.New384() + case "sha-512/224": + h = sha512.New512_224() + case "sha-512/256": + h = sha512.New512_256() + case "sha3-224": + h = crypto.SHA3_224.New() + case "sha3-256": + h = crypto.SHA3_256.New() + case "sha3-384": + h = crypto.SHA3_384.New() + case "sha3-512": + h = crypto.SHA3_512.New() + default: + return nil, errors.New("unsupported _sd_alg: " + s.body["_sd_alg"].(string)) + } + + for { + var indexesFound []int + for i := 0; i < len(disclosuresToCheck); i++ { + d := disclosuresToCheck[i] + + h.Write([]byte(d.EncodedValue())) + hashedDisclosures := h.Sum(nil) + base64HashedDisclosureBytes := make([]byte, base64.RawURLEncoding.EncodedLen(len(hashedDisclosures))) + base64.RawURLEncoding.Encode(base64HashedDisclosureBytes, hashedDisclosures) + + found, err := validateSDClaims(s.Body(), &d, string(base64HashedDisclosureBytes)) + if err != nil { + return nil, err + } - found, err := validateSDClaims(s.Body(), &d, string(base64HashedDisclosureBytes)) - if err != nil { - return nil, err + if found { + indexesFound = append(indexesFound, i) + } + h.Reset() } - if !found { - return nil, errors.New("no matching digest found: " + d.RawValue() + " encoded: " + string(base64HashedDisclosureBytes)) + if len(indexesFound) == 0 { + return nil, fmt.Errorf("no matching digest found for: %v", stringifyDisclosures(disclosuresToCheck)) + } + slices.Sort(indexesFound) + slices.Reverse(indexesFound) + for _, i := range indexesFound { + disclosuresToCheck = append(disclosuresToCheck[:i], disclosuresToCheck[i+1:]...) } + if len(disclosuresToCheck) == 0 { + break + } + } + + bodyMap := stripSDClaims(*s.Body()) + + return bodyMap, nil +} - if len(disclosuresToCheck) > 1 { - disclosuresToCheck = disclosuresToCheck[1:] +func stringifyDisclosures(disclosures []Disclosure) string { + result := "[" + for i, d := range disclosures { + if d.ClaimName() != nil { + result += "(" + *d.ClaimName() + ") " } else { - disclosuresToCheck = []Disclosure{} //empty to-check array + result += " " + } + result += d.ClaimValue() + " " + if i != len(disclosures)-1 { + result += "," } - } + result += "]" + return result +} - for k, v := range s.body { - if k != "_sd" && k != "_sd_alg" { - bodyMap[k] = v +func stripSDClaims(body map[string]any) map[string]any { + bodyMap := make(map[string]any) + for k, v := range body { + switch reflect.TypeOf(v).Kind() { + case reflect.Map: + bodyMap[k] = stripSDClaims(v.(map[string]any)) + case reflect.Slice: + if k != "_sd" { + bodyMap[k] = stripSDClaimsFromSlice(v.([]any)) + } + default: + if k != "_sd_alg" { + bodyMap[k] = v + } } } + return bodyMap +} - return bodyMap, nil +func stripSDClaimsFromSlice(input []any) []any { + for i, v := range input { + switch reflect.TypeOf(v).Kind() { + case reflect.Map: + input[i] = stripSDClaims(v.(map[string]any)) + case reflect.Slice: + input[i] = stripSDClaimsFromSlice(v.([]any)) + } + } + return input } func validateSignature(head map[string]any, signedBody, signature string, publicKey string) (bool, error) { @@ -517,7 +575,8 @@ func parseClaimValue(cv string) (any, error) { func validateSDClaims(values *map[string]any, currentDisclosure *Disclosure, base64HashedDisclosure string) (found bool, err error) { if _, ok := (*values)["_sd"]; ok { for _, digest := range (*values)["_sd"].([]any) { - if digest == base64HashedDisclosure { + sDigest := digest.(string) + if sDigest == base64HashedDisclosure { if currentDisclosure.ClaimName() != nil { val, err := parseClaimValue(currentDisclosure.ClaimValue()) if err != nil { @@ -556,130 +615,53 @@ func validateSDClaims(values *map[string]any, currentDisclosure *Disclosure, bas func validateArrayClaims(s *[]any, currentDisclosure *Disclosure, base64HashedDisclosure string) (found bool, err error) { for i, v := range *s { - ad := &arrayDisclosure{} - vb, err := json.Marshal(v) - if err != nil { - return false, err - } - _ = json.Unmarshal(vb, ad) + switch reflect.TypeOf(v).Kind() { - if ad.Digest != nil { - if *ad.Digest == base64HashedDisclosure { - (*s)[i] = currentDisclosure.ClaimValue() + case reflect.Slice: + found, err = validateArrayClaims(PointerSlice(v.([]any)), currentDisclosure, base64HashedDisclosure) + if err != nil { + return false, err + } + if found { return true, nil } - } - if reflect.TypeOf(v).Kind() == reflect.Slice { - found, err = validateArrayClaims(PointerSlice(v.([]any)), currentDisclosure, base64HashedDisclosure) + case reflect.Map: + ad := &arrayDisclosure{} + vb, err := json.Marshal(v) if err != nil { - return found, err + return false, err + } + + _ = json.Unmarshal(vb, ad) + + if ad.Digest != nil { + if *ad.Digest == base64HashedDisclosure { + if strings.Contains(currentDisclosure.ClaimValue(), "{") || strings.Contains(currentDisclosure.ClaimValue(), "[") { + var m map[string]any + err = json.Unmarshal([]byte(currentDisclosure.ClaimValue()), &m) + if err != nil { + return false, err + } + (*s)[i] = m + } else { + (*s)[i] = currentDisclosure.ClaimValue() + } + + return true, nil + } } - } - if reflect.TypeOf(v).Kind() == reflect.Map { found, err = validateSDClaims(PointerMap(v.(map[string]any)), currentDisclosure, base64HashedDisclosure) if err != nil { - return found, err + return false, err + } + if found { + return true, nil } } } return false, nil } - -// Body returns the body of the JWT -func (s *SdJwt) Body() *map[string]any { - return &s.body -} - -// Token returns the JWT token as it was received -func (s *SdJwt) Token() string { - return s.token -} - -// Signature returns the signature of the provided token used to verify it -func (s *SdJwt) Signature() string { - return s.signature -} - -// Head returns the head of the JWT -func (s *SdJwt) Head() map[string]any { - return s.head -} - -// Disclosures returns the disclosures of the SD-JWT -func (s *SdJwt) Disclosures() []Disclosure { - return s.disclosures -} - -// PublicKey returns the public key json (if provided) -func (s *SdJwt) PublicKey() string { - return s.publicKey -} - -// KbJwt returns the signed kb-jwt (if provided) -func (s *SdJwt) KbJwt() *string { - return s.kbJwt -} - -// ClaimName returns the claim name of the disclosure -func (d *Disclosure) ClaimName() *string { - return d.claimName -} - -// ClaimValue returns the claim value of the disclosure -func (d *Disclosure) ClaimValue() string { - return d.claimValue -} - -// Salt returns the salt of the disclosure -func (d *Disclosure) Salt() string { - return d.salt -} - -// RawValue returns the decoded contents of the disclosure -func (d *Disclosure) RawValue() string { - return d.rawValue -} - -// EncodedValue returns the disclosure as it was listed in the original SD-JWT -func (d *Disclosure) EncodedValue() string { - return d.encodedValue -} - -func (d *Disclosure) setClaimName(claimName *string) { - d.claimName = claimName -} - -func (d *Disclosure) setClaimValue(claimValue string) { - d.claimValue = claimValue -} - -func (d *Disclosure) setSalt(salt string) { - d.salt = salt -} - -func (d *Disclosure) setRawValue(rawValue string) { - d.rawValue = rawValue -} - -func (d *Disclosure) setEncodedValue(encodedValue string) { - d.encodedValue = encodedValue -} - -// Pointer is a helper method that returns a pointer to the given value. -func Pointer[T comparable](t T) *T { - return &t -} - -// PointerMap is a helper method that returns a pointer to the given map. -func PointerMap(m map[string]any) *map[string]any { - return &m -} - -// PointerSlice is a helper method that returns a pointer to the given slice. -func PointerSlice(s []any) *[]any { - return &s -} diff --git a/sd-jwt_test.go b/sd-jwt_test.go index b38fd34..a2e475a 100644 --- a/sd-jwt_test.go +++ b/sd-jwt_test.go @@ -2,6 +2,7 @@ package go_sd_jwt_test import ( "encoding/json" + "fmt" go_sd_jwt "github.com/MichaelFraser99/go-sd-jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -16,164 +17,317 @@ func TestMain(m *testing.M) { var examplePublicKey = "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ\",\"y\":\"Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8\"}" func TestFromToken(t *testing.T) { - exampleJwt := "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFrb2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjogInVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~" + tests := []struct { + name string + token string + validate func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) + }{ + { + name: "valid token", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFrb2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjogInVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + if err != nil { + t.Error("error should be nil", err) + } + if sdJwt == nil { + t.Error("sdJwt should not be nil") + } + if sdJwt.Token() == "" { + t.Error("token should not be empty") + } + if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { + t.Error("head should not be empty") + } + if sdJwt.Body() == nil { + t.Error("body should not be empty") + } + if sdJwt.Signature() == "" { + t.Error("signature should not be empty") + } + if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { + t.Error("disclosures should not be empty") + } + if len(sdJwt.Disclosures()) != 10 { + t.Error("disclosures should have 10 elements") + } + if sdJwt.KbJwt() != nil { + t.Error("kbJwt should be nil:", *sdJwt.KbJwt()) + } - sdJwt, err := go_sd_jwt.FromToken(exampleJwt, examplePublicKey) - if err != nil { - t.Error("error should be nil", err) - } - if sdJwt == nil { - t.Error("sdJwt should not be nil") - } - if sdJwt.Token() == "" { - t.Error("token should not be empty") - } - if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { - t.Error("head should not be empty") - } - if sdJwt.Body() == nil { - t.Error("body should not be empty") - } - if sdJwt.Signature() == "" { - t.Error("signature should not be empty") - } - if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { - t.Error("disclosures should not be empty") - } - if len(sdJwt.Disclosures()) != 10 { - t.Error("disclosures should have 10 elements") - } - if sdJwt.KbJwt() != nil { - t.Error("kbJwt should be nil:", *sdJwt.KbJwt()) - } + claims, err := sdJwt.GetDisclosedClaims() + require.NoError(t, err) - claims, err := sdJwt.GetDisclosedClaims() - require.NoError(t, err) - - b, _ := json.Marshal(claims) - t.Log(string(b)) - - assert.Nil(t, claims["_sd"]) - assert.Nil(t, claims["_sd_alg"]) - assert.Equal(t, 1570000000, claims["updated_at"]) - assert.Len(t, claims["nationalities"], 2) - assert.Contains(t, claims["nationalities"], "DE") - assert.Contains(t, claims["nationalities"], "US") - assert.Equal(t, "1940-01-01", claims["birthdate"]) - assert.NotNil(t, claims["address"]) - assert.Equal(t, "123 Main St", claims["address"].(map[string]interface{})["street_address"]) - assert.Equal(t, "Anytown", claims["address"].(map[string]interface{})["locality"]) - assert.Equal(t, "Anystate", claims["address"].(map[string]interface{})["region"]) - assert.Equal(t, "US", claims["address"].(map[string]interface{})["country"]) - assert.True(t, claims["phone_number_verified"].(bool)) - assert.Equal(t, "+1-202-555-0101", claims["phone_number"]) - assert.Equal(t, "johndoe@example.com", claims["email"]) - assert.Equal(t, "John", claims["given_name"]) - assert.Equal(t, "Doe", claims["family_name"]) - assert.Equal(t, "user_42", claims["sub"]) -} + b, _ := json.Marshal(claims) + t.Log(string(b)) -func TestFromToken_KBJwt(t *testing.T) { - exampleJwt := "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIjA5VGJTdW8xMmkyQ3FaYmczMUFGZ2JHeV9Vbk1JWElIb01qc0VMcHVrcWciLCAiMG45eXpGU1d2S19CVUhpYU1obTEyZ2hyQ3RWYWhyR0o2Xy1rWlAteVNxNCIsICI0Vm9BM2ExVm1QeG1kQzhXSW4zcE9xUWYzZ2ZPVk92RFlzTjVFNVI1S2QwIiwgIjVBODhBbWF1QWFvLVFBTmFvOTVDWVVrVVBOVGlkX2dBSzhhWXRaOVJad2MiLCAiOTEwYnlyM1VWUnFSelFvUHpCc2MyMG0tZU1ncFpBaExONno4Tm9HRjVtYyIsICJDaC1EQmNMM2tiNFZiSEl3dGtublpkTlVIdGhFcTlNWmpvRmRnNmlkaWhvIiwgIkkwMGZjRlVvRFhDdWNwNXl5MnVqcVBzc0RWR2FXTmlVbGlOel9hd0QwZ2MiLCAiWDlNYVBhRldtUVlwZkhFZHl0UmRhY2xuWW9FcnU4RXp0QkVVUXVXT2U0NCIsICJZMXVyV0pWXy1IQkduU2Y5dEZPd3ZINGNJQ1JCQ2lLd0VIZmtYRlNmanBvIiwgInJOaEtvcmFhcS0teDdCV1dJVmhiR1h1MVhYWExNOGl2WlhEM20yRlpNZ3MiLCAieHBzcTZjeFFIRHNPblpXaHJxQmNrVGtPTV9lZkVsVW5ERlhPRm1vd0xTRSIsICJ6VTQ1MmxrR2JFS2g4WnVIXzhLeDNDVXZuMUY0eTFnWkxxbERUZ1hfOFBrIl0sICJpc3MiOiAiaHR0cHM6Ly9waWQtcHJvdmlkZXIubWVtYmVyc3RhdGUuZXhhbXBsZS5ldSIsICJpYXQiOiAxNTQxNDkzNzI0LCAiZXhwIjogMTg4MzAwMDAwMCwgInR5cGUiOiAiUGVyc29uSWRlbnRpZmljYXRpb25EYXRhIiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ.K5Ol1OgKQtP3FGBoJ8pmPdraIsSeOHxOAE-64L3Bc3q_aq2ANQSRNh4hqPYjuK4CnqyCK1reyHLO2iiMDwleOw~WyJzLXpVaXE1azFyU0dSb1hQUE5rMzVRIiwgImlzX292ZXJfMTgiLCB0cnVlXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgIm5hdGlvbmFsaXRpZXMiLCBbeyIuLi4iOiAiSnVMMzJRWER6aXpsLUw2Q0xyZnhmanBac1gzTzZ2c2ZwQ1ZkMWprd0pZZyJ9XV0~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgIkRFIl0~eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY3ODkwIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE2ODgxNjA0ODN9.duRIKesDpGY-5GkRcr98uhud64PfmPhL0qMcXFeBL5x2IGbAc_buglOrpd0LZA_cgCGXDx4zQoMou2kKrl-WCA" + assert.Nil(t, claims["_sd"]) + assert.Nil(t, claims["_sd_alg"]) + assert.Equal(t, 1570000000, claims["updated_at"]) + assert.Len(t, claims["nationalities"], 2) + assert.Contains(t, claims["nationalities"], "DE") + assert.Contains(t, claims["nationalities"], "US") + assert.Equal(t, "1940-01-01", claims["birthdate"]) + assert.NotNil(t, claims["address"]) + assert.Equal(t, "123 Main St", claims["address"].(map[string]any)["street_address"]) + assert.Equal(t, "Anytown", claims["address"].(map[string]any)["locality"]) + assert.Equal(t, "Anystate", claims["address"].(map[string]any)["region"]) + assert.Equal(t, "US", claims["address"].(map[string]any)["country"]) + assert.True(t, claims["phone_number_verified"].(bool)) + assert.Equal(t, "+1-202-555-0101", claims["phone_number"]) + assert.Equal(t, "johndoe@example.com", claims["email"]) + assert.Equal(t, "John", claims["given_name"]) + assert.Equal(t, "Doe", claims["family_name"]) + assert.Equal(t, "user_42", claims["sub"]) + }, + }, + { + name: "valid token more complex structure", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkM5aW5wNllvUmFFWFI0Mjd6WUpQN1FyazFXSF84YmR3T0FfWVVyVW5HUVUiLCAiS3VldDF5QWEwSElRdlluT1ZkNTloY1ZpTzlVZzZKMmtTZnFZUkJlb3d2RSIsICJNTWxkT0ZGekIyZDB1bWxtcFRJYUdlcmhXZFVfUHBZZkx2S2hoX2ZfOWFZIiwgIlg2WkFZT0lJMnZQTjQwVjd4RXhad1Z3ejd5Um1MTmNWd3Q1REw4Ukx2NGciLCAiWTM0em1JbzBRTExPdGRNcFhHd2pCZ0x2cjE3eUVoaFlUMEZHb2ZSLWFJRSIsICJmeUdwMFdUd3dQdjJKRFFsbjFsU2lhZW9iWnNNV0ExMGJRNTk4OS05RFRzIiwgIm9tbUZBaWNWVDhMR0hDQjB1eXd4N2ZZdW8zTUhZS08xNWN6LVJaRVlNNVEiLCAiczBCS1lzTFd4UVFlVTh0VmxsdE03TUtzSVJUckVJYTFQa0ptcXhCQmY1VSJdLCAiaXNzIjogImh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAiYWRkcmVzcyI6IHsiX3NkIjogWyI2YVVoelloWjdTSjFrVm1hZ1FBTzN1MkVUTjJDQzFhSGhlWnBLbmFGMF9FIiwgIkF6TGxGb2JrSjJ4aWF1cFJFUHlvSnotOS1OU2xkQjZDZ2pyN2ZVeW9IemciLCAiUHp6Y1Z1MHFiTXVCR1NqdWxmZXd6a2VzRDl6dXRPRXhuNUVXTndrclEtayIsICJiMkRrdzBqY0lGOXJHZzhfUEY4WmN2bmNXN3p3Wmo1cnlCV3ZYZnJwemVrIiwgImNQWUpISVo4VnUtZjlDQ3lWdWIyVWZnRWs4anZ2WGV6d0sxcF9KbmVlWFEiLCAiZ2xUM2hyU1U3ZlNXZ3dGNVVEWm1Xd0JUdzMyZ25VbGRJaGk4aEdWQ2FWNCIsICJydkpkNmlxNlQ1ZWptc0JNb0d3dU5YaDlxQUFGQVRBY2k0MG9pZEVlVnNBIiwgInVOSG9XWWhYc1poVkpDTkUyRHF5LXpxdDd0NjlnSkt5NVFhRnY3R3JNWDQiXX0sICJfc2RfYWxnIjogInNoYS0yNTYifQ.IjE4EfnYu1RZ1uz6yqtFh5Lppq36VC4VeSr-hLDFpZ9zqBNmMrT5JHLLXTuMJqKQp3NIzDsLaft4GK5bYyfqhg~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImNvdW50cnkiLCAiSlAiXQ~", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + if err != nil { + t.Error("error should be nil", err) + } + if sdJwt == nil { + t.Error("sdJwt should not be nil") + } + if sdJwt.Token() == "" { + t.Error("token should not be empty") + } + if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { + t.Error("head should not be empty") + } + if sdJwt.Body() == nil { + t.Error("body should not be empty") + } + if sdJwt.Signature() == "" { + t.Error("signature should not be empty") + } + if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { + t.Error("disclosures should not be empty") + } + if len(sdJwt.Disclosures()) != 2 { + t.Error("disclosures should have 2 elements") + } + if sdJwt.KbJwt() != nil { + t.Error("kbJwt should be nil:", *sdJwt.KbJwt()) + } - sdJwt, err := go_sd_jwt.FromToken(exampleJwt, examplePublicKey) - if err != nil { - t.Error("error should be nil:", err) - } - if sdJwt == nil { - t.Error("sdJwt should not be nil") - } - if sdJwt.Token() == "" { - t.Error("token should not be empty") - } - if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { - t.Error("head should not be empty") - } - if sdJwt.Body() == nil { - t.Error("body should not be empty") - } - if sdJwt.Signature() == "" { - t.Error("signature should not be empty") - } - if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { - t.Error("disclosures should not be empty") - } - if len(sdJwt.Disclosures()) != 3 { - t.Error("disclosures should have 10 elements:", len(sdJwt.Disclosures())) - } - if sdJwt.KbJwt() == nil { - t.Error("kbJwt should not be nil") - } + claims, err := sdJwt.GetDisclosedClaims() + require.NoError(t, err) - claims, err := sdJwt.GetDisclosedClaims() - require.NoError(t, err) - - b, _ := json.Marshal(claims) - t.Log(string(b)) - - assert.Nil(t, claims["_sd"]) - assert.Nil(t, claims["_sd_alg"]) - assert.NotNil(t, claims["cnf"]) - assert.NotNil(t, claims["cnf"].(map[string]interface{})["jwk"]) - assert.Len(t, claims["nationalities"], 1) - assert.Contains(t, claims["nationalities"], "DE") - assert.True(t, claims["is_over_18"].(bool)) - assert.Equal(t, "https://pid-provider.memberstate.example.eu", claims["iss"]) - assert.Equal(t, "PersonIdentificationData", claims["type"]) -} + b, _ := json.Marshal(claims) + t.Log(string(b)) -func TestFromToken_Jws(t *testing.T) { - exampleJwt := "{\"payload\": \"eyJfc2QiOiBbIjRIQm42YUlZM1d0dUdHV1R4LXFVajZjZGs2V0JwWnlnbHRkRmF2UGE3TFkiLCAiOHNtMVFDZjAyMXBObkhBQ0k1c1A0bTRLWmd5Tk9PQVljVGo5SE5hQzF3WSIsICJTRE43OU5McEFuSFBta3JkZVlkRWE4OVhaZHNrME04REtZU1FPVTJaeFFjIiwgIlh6RnJ6d3NjTTZHbjZDSkRjNnZWSzhCa01uZkc4dk9TS2ZwUElaZEFmZEUiLCAiZ2JPc0k0RWRxMngyS3ctdzV3UEV6YWtvYjloVjFjUkQwQVROM29RTDlKTSIsICJqTUNYVnotLTliOHgzN1ljb0RmWFFpbnp3MXdaY2NjZkZSQkNGR3FkRzJvIiwgIm9LSTFHZDJmd041V3d2amxGa29oaWRHdmltLTMxT3VsUjNxMGhyRE8wNzgiXSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNjgzMDAwMDAwLCAiZXhwIjogMTg4MzAwMDAwMCwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ\",\"protected\": \"eyJhbGciOiAiRVMyNTYifQ\",\"signature\": \"qNNvkravlssjHS8TSnj5lAFc5on6MjG0peMt8Zjh1Yefxn0DxkcVOU9r7t1VNehJISOFL7NuJ5V27DVbNJBLoA\",\"disclosures\": [\"WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgInN1YiIsICJqb2huX2RvZV80MiJd\",\"WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImdpdmVuX25hbWUiLCAiSm9obiJd\",\"WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd\",\"WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ\",\"WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ\",\"WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0\",\"WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0\"]}" - - sdJwt, err := go_sd_jwt.FromToken(exampleJwt, examplePublicKey) - require.NoError(t, err) - require.NotNil(t, sdJwt) - assert.NotEmpty(t, sdJwt.Token()) - assert.NotEmpty(t, sdJwt.Head()) - assert.NotEmpty(t, sdJwt.Body()) - assert.NotEmpty(t, sdJwt.Signature()) - assert.NotEmpty(t, sdJwt.Disclosures()) - assert.Len(t, sdJwt.Disclosures(), 7) - assert.Nil(t, sdJwt.KbJwt()) - - claims, err := sdJwt.GetDisclosedClaims() - require.NoError(t, err) - - b, _ := json.Marshal(claims) - t.Log(string(b)) - - assert.Nil(t, claims["_sd"]) - assert.Nil(t, claims["_sd_alg"]) - assert.Equal(t, "1940-01-01", claims["birthdate"]) - assert.NotNil(t, claims["address"]) - assert.Equal(t, "123 Main St", claims["address"].(map[string]interface{})["street_address"]) - assert.Equal(t, "Anytown", claims["address"].(map[string]interface{})["locality"]) - assert.Equal(t, "Anystate", claims["address"].(map[string]interface{})["region"]) - assert.Equal(t, "US", claims["address"].(map[string]interface{})["country"]) - assert.Equal(t, "+1-202-555-0101", claims["phone_number"]) - assert.Equal(t, "johndoe@example.com", claims["email"]) - assert.Equal(t, "John", claims["given_name"]) - assert.Equal(t, "Doe", claims["family_name"]) - assert.Equal(t, "john_doe_42", claims["sub"]) -} + assert.Nil(t, claims["_sd"]) + assert.Nil(t, claims["_sd_alg"]) + assert.Equal(t, float64(1883000000), claims["exp"]) + assert.Equal(t, float64(1683000000), claims["iat"]) + assert.Equal(t, "https://issuer.example.com", claims["iss"]) + assert.NotNil(t, claims["address"]) + assert.Nil(t, claims["address"].(map[string]any)["_sd"]) + assert.Equal(t, "JP", claims["address"].(map[string]any)["country"]) + assert.Equal(t, "\\u6e2f\\u533a", claims["address"].(map[string]any)["region"]) + }, + }, + { + name: "valid token with a very complex structure", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIi1hU3puSWQ5bVdNOG9jdVFvbENsbHN4VmdncTEtdkhXNE90bmhVdFZtV3ciLCAiSUticllObjN2QTdXRUZyeXN2YmRCSmpERFVfRXZRSXIwVzE4dlRScFVTZyIsICJvdGt4dVQxNG5CaXd6TkozTVBhT2l0T2w5cFZuWE9hRUhhbF94a3lOZktJIl0sICJpc3MiOiAiaHR0cHM6Ly9pc3N1ZXIuZXhhbXBsZS5jb20iLCAiaWF0IjogMTY4MzAwMDAwMCwgImV4cCI6IDE4ODMwMDAwMDAsICJ2ZXJpZmllZF9jbGFpbXMiOiB7InZlcmlmaWNhdGlvbiI6IHsiX3NkIjogWyI3aDRVRTlxU2N2REtvZFhWQ3VvS2ZLQkpwVkJmWE1GX1RtQUdWYVplM1NjIiwgInZUd2UzcmFISUZZZ0ZBM3hhVUQyYU14Rno1b0RvOGlCdTA1cUtsT2c5THciXSwgInRydXN0X2ZyYW1ld29yayI6ICJkZV9hbWwiLCAiZXZpZGVuY2UiOiBbeyIuLi4iOiAidFlKMFREdWN5WlpDUk1iUk9HNHFSTzV2a1BTRlJ4RmhVRUxjMThDU2wzayJ9XX0sICJjbGFpbXMiOiB7Il9zZCI6IFsiUmlPaUNuNl93NVpIYWFka1FNcmNRSmYwSnRlNVJ3dXJSczU0MjMxRFRsbyIsICJTXzQ5OGJicEt6QjZFYW5mdHNzMHhjN2NPYW9uZVJyM3BLcjdOZFJtc01vIiwgIldOQS1VTks3Rl96aHNBYjlzeVdPNklJUTF1SGxUbU9VOHI4Q3ZKMGNJTWsiLCAiV3hoX3NWM2lSSDliZ3JUQkppLWFZSE5DTHQtdmpoWDFzZC1pZ09mXzlsayIsICJfTy13SmlIM2VuU0I0Uk9IbnRUb1FUOEptTHR6LW1oTzJmMWM4OVhvZXJRIiwgImh2RFhod21HY0pRc0JDQTJPdGp1TEFjd0FNcERzYVUwbmtvdmNLT3FXTkUiXX19LCAiX3NkX2FsZyI6ICJzaGEtMjU2In0.kbfpTas9_-dLMgyeUxIXuBGLtCZUO2bG9JA7v73ebzpX1LA5MBtQsyZZut-Bm3_TW8sTqLCDPUN4ZC5pKCyQig~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgInRpbWUiLCAiMjAxMi0wNC0yM1QxODoyNVoiXQ~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgeyJfc2QiOiBbIjl3cGpWUFd1RDdQSzBuc1FETDhCMDZsbWRnVjNMVnliaEh5ZFFwVE55TEkiLCAiRzVFbmhPQU9vVTlYXzZRTU52ekZYanBFQV9SYy1BRXRtMWJHX3djYUtJayIsICJJaHdGcldVQjYzUmNacTl5dmdaMFhQYzdHb3doM08ya3FYZUJJc3dnMUI0IiwgIldweFE0SFNvRXRjVG1DQ0tPZURzbEJfZW11Y1lMejJvTzhvSE5yMWJFVlEiXX1d~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgIm1ldGhvZCIsICJwaXBwIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgImdpdmVuX25hbWUiLCAiTWF4Il0~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImZhbWlseV9uYW1lIiwgIk1cdTAwZmNsbGVyIl0~WyJ5MXNWVTV3ZGZKYWhWZGd3UGdTN1JRIiwgImFkZHJlc3MiLCB7ImxvY2FsaXR5IjogIk1heHN0YWR0IiwgInBvc3RhbF9jb2RlIjogIjEyMzQ0IiwgImNvdW50cnkiOiAiREUiLCAic3RyZWV0X2FkZHJlc3MiOiAiV2VpZGVuc3RyYVx1MDBkZmUgMjIifV0~", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + if err != nil { + t.Error("error should be nil", err) + } + if sdJwt == nil { + t.Error("sdJwt should not be nil") + } + if sdJwt.Token() == "" { + t.Error("token should not be empty") + } + if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { + t.Error("head should not be empty") + } + if sdJwt.Body() == nil { + t.Error("body should not be empty") + } + if sdJwt.Signature() == "" { + t.Error("signature should not be empty") + } + if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { + t.Error("disclosures should not be empty") + } + if len(sdJwt.Disclosures()) != 6 { + t.Error("disclosures should have 6 elements, has", len(sdJwt.Disclosures())) + } + if sdJwt.KbJwt() != nil { + t.Error("kbJwt should be nil:", *sdJwt.KbJwt()) + } -func TestFromToken_DuplicateDisclosure(t *testing.T) { - exampleJwt := "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFrb2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjogInVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~" + claims, err := sdJwt.GetDisclosedClaims() + require.NoError(t, err) - sdJwt, err := go_sd_jwt.FromToken(exampleJwt, examplePublicKey) - if err == nil { - t.Error("error should be thrown") - t.FailNow() - } - if sdJwt != nil { - t.Error("sdJwt should be nil: ", sdJwt) + b, _ := json.Marshal(claims) + t.Log(string(b)) + + assert.Equal(t, 4, len(claims)) + assert.Nil(t, claims["_sd"]) + assert.Nil(t, claims["_sd_alg"]) + assert.Equal(t, float64(1883000000), claims["exp"]) + assert.Equal(t, float64(1683000000), claims["iat"]) + assert.Equal(t, "https://issuer.example.com", claims["iss"]) + assert.NotNil(t, claims["verified_claims"]) + assert.Equal(t, 2, len(claims["verified_claims"].(map[string]any))) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["verification"]) + assert.Equal(t, 3, len(claims["verified_claims"].(map[string]any)["verification"].(map[string]any))) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["trust_framework"]) + assert.Equal(t, "de_aml", claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["trust_framework"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["evidence"]) + assert.Equal(t, 1, len(claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["evidence"].([]any))) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["evidence"].([]any)[0].(map[string]any)["method"]) + assert.Equal(t, 1, len(claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["evidence"].([]any)[0].(map[string]any))) + assert.Equal(t, "pipp", claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["evidence"].([]any)[0].(map[string]any)["method"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["time"]) + assert.Equal(t, "2012-04-23T18:25Z", claims["verified_claims"].(map[string]any)["verification"].(map[string]any)["time"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"]) + assert.Equal(t, 3, len(claims["verified_claims"].(map[string]any)["claims"].(map[string]any))) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["given_name"]) + assert.Equal(t, "Max", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["given_name"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["family_name"]) + assert.Equal(t, "M\\u00fcller", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["family_name"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"]) + assert.Equal(t, 4, len(claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any))) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["locality"]) + assert.Equal(t, "Maxstadt", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["locality"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["postal_code"]) + assert.Equal(t, "12344", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["postal_code"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["country"]) + assert.Equal(t, "DE", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["country"]) + assert.NotNil(t, claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["street_address"]) + assert.Equal(t, "Weidenstraße 22", claims["verified_claims"].(map[string]any)["claims"].(map[string]any)["address"].(map[string]any)["street_address"]) + }, + }, + { + name: "valid token with valid key-bound jwt", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIjA5VGJTdW8xMmkyQ3FaYmczMUFGZ2JHeV9Vbk1JWElIb01qc0VMcHVrcWciLCAiMG45eXpGU1d2S19CVUhpYU1obTEyZ2hyQ3RWYWhyR0o2Xy1rWlAteVNxNCIsICI0Vm9BM2ExVm1QeG1kQzhXSW4zcE9xUWYzZ2ZPVk92RFlzTjVFNVI1S2QwIiwgIjVBODhBbWF1QWFvLVFBTmFvOTVDWVVrVVBOVGlkX2dBSzhhWXRaOVJad2MiLCAiOTEwYnlyM1VWUnFSelFvUHpCc2MyMG0tZU1ncFpBaExONno4Tm9HRjVtYyIsICJDaC1EQmNMM2tiNFZiSEl3dGtublpkTlVIdGhFcTlNWmpvRmRnNmlkaWhvIiwgIkkwMGZjRlVvRFhDdWNwNXl5MnVqcVBzc0RWR2FXTmlVbGlOel9hd0QwZ2MiLCAiWDlNYVBhRldtUVlwZkhFZHl0UmRhY2xuWW9FcnU4RXp0QkVVUXVXT2U0NCIsICJZMXVyV0pWXy1IQkduU2Y5dEZPd3ZINGNJQ1JCQ2lLd0VIZmtYRlNmanBvIiwgInJOaEtvcmFhcS0teDdCV1dJVmhiR1h1MVhYWExNOGl2WlhEM20yRlpNZ3MiLCAieHBzcTZjeFFIRHNPblpXaHJxQmNrVGtPTV9lZkVsVW5ERlhPRm1vd0xTRSIsICJ6VTQ1MmxrR2JFS2g4WnVIXzhLeDNDVXZuMUY0eTFnWkxxbERUZ1hfOFBrIl0sICJpc3MiOiAiaHR0cHM6Ly9waWQtcHJvdmlkZXIubWVtYmVyc3RhdGUuZXhhbXBsZS5ldSIsICJpYXQiOiAxNTQxNDkzNzI0LCAiZXhwIjogMTg4MzAwMDAwMCwgInR5cGUiOiAiUGVyc29uSWRlbnRpZmljYXRpb25EYXRhIiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ.K5Ol1OgKQtP3FGBoJ8pmPdraIsSeOHxOAE-64L3Bc3q_aq2ANQSRNh4hqPYjuK4CnqyCK1reyHLO2iiMDwleOw~WyJzLXpVaXE1azFyU0dSb1hQUE5rMzVRIiwgImlzX292ZXJfMTgiLCB0cnVlXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgIm5hdGlvbmFsaXRpZXMiLCBbeyIuLi4iOiAiSnVMMzJRWER6aXpsLUw2Q0xyZnhmanBac1gzTzZ2c2ZwQ1ZkMWprd0pZZyJ9XV0~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgIkRFIl0~eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY3ODkwIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE2ODgxNjA0ODN9.duRIKesDpGY-5GkRcr98uhud64PfmPhL0qMcXFeBL5x2IGbAc_buglOrpd0LZA_cgCGXDx4zQoMou2kKrl-WCA", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + if err != nil { + t.Error("error should be nil:", err) + } + if sdJwt == nil { + t.Error("sdJwt should not be nil") + } + if sdJwt.Token() == "" { + t.Error("token should not be empty") + } + if sdJwt.Head() == nil || len(sdJwt.Head()) == 0 { + t.Error("head should not be empty") + } + if sdJwt.Body() == nil { + t.Error("body should not be empty") + } + if sdJwt.Signature() == "" { + t.Error("signature should not be empty") + } + if sdJwt.Disclosures() == nil || len(sdJwt.Disclosures()) == 0 { + t.Error("disclosures should not be empty") + } + if len(sdJwt.Disclosures()) != 3 { + t.Error("disclosures should have 10 elements:", len(sdJwt.Disclosures())) + } + if sdJwt.KbJwt() == nil { + t.Error("kbJwt should not be nil") + } + + claims, err := sdJwt.GetDisclosedClaims() + require.NoError(t, err) + + b, _ := json.Marshal(claims) + t.Log(string(b)) + + assert.Nil(t, claims["_sd"]) + assert.Nil(t, claims["_sd_alg"]) + assert.NotNil(t, claims["cnf"]) + assert.NotNil(t, claims["cnf"].(map[string]any)["jwk"]) + assert.Len(t, claims["nationalities"], 1) + assert.Contains(t, claims["nationalities"], "DE") + assert.True(t, claims["is_over_18"].(bool)) + assert.Equal(t, "https://pid-provider.memberstate.example.eu", claims["iss"]) + assert.Equal(t, "PersonIdentificationData", claims["type"]) + }, + }, + { + name: "valid jws token", + token: "{\"payload\": \"eyJfc2QiOiBbIjRIQm42YUlZM1d0dUdHV1R4LXFVajZjZGs2V0JwWnlnbHRkRmF2UGE3TFkiLCAiOHNtMVFDZjAyMXBObkhBQ0k1c1A0bTRLWmd5Tk9PQVljVGo5SE5hQzF3WSIsICJTRE43OU5McEFuSFBta3JkZVlkRWE4OVhaZHNrME04REtZU1FPVTJaeFFjIiwgIlh6RnJ6d3NjTTZHbjZDSkRjNnZWSzhCa01uZkc4dk9TS2ZwUElaZEFmZEUiLCAiZ2JPc0k0RWRxMngyS3ctdzV3UEV6YWtvYjloVjFjUkQwQVROM29RTDlKTSIsICJqTUNYVnotLTliOHgzN1ljb0RmWFFpbnp3MXdaY2NjZkZSQkNGR3FkRzJvIiwgIm9LSTFHZDJmd041V3d2amxGa29oaWRHdmltLTMxT3VsUjNxMGhyRE8wNzgiXSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNjgzMDAwMDAwLCAiZXhwIjogMTg4MzAwMDAwMCwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ\",\"protected\": \"eyJhbGciOiAiRVMyNTYifQ\",\"signature\": \"qNNvkravlssjHS8TSnj5lAFc5on6MjG0peMt8Zjh1Yefxn0DxkcVOU9r7t1VNehJISOFL7NuJ5V27DVbNJBLoA\",\"disclosures\": [\"WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgInN1YiIsICJqb2huX2RvZV80MiJd\",\"WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImdpdmVuX25hbWUiLCAiSm9obiJd\",\"WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd\",\"WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ\",\"WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ\",\"WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0\",\"WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0\"]}", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + require.NoError(t, err) + require.NotNil(t, sdJwt) + assert.NotEmpty(t, sdJwt.Token()) + assert.NotEmpty(t, sdJwt.Head()) + assert.NotEmpty(t, sdJwt.Body()) + assert.NotEmpty(t, sdJwt.Signature()) + assert.NotEmpty(t, sdJwt.Disclosures()) + assert.Len(t, sdJwt.Disclosures(), 7) + assert.Nil(t, sdJwt.KbJwt()) + + claims, err := sdJwt.GetDisclosedClaims() + require.NoError(t, err) + + b, _ := json.Marshal(claims) + t.Log(string(b)) + + assert.Nil(t, claims["_sd"]) + assert.Nil(t, claims["_sd_alg"]) + assert.Equal(t, "1940-01-01", claims["birthdate"]) + assert.NotNil(t, claims["address"]) + assert.Equal(t, "123 Main St", claims["address"].(map[string]any)["street_address"]) + assert.Equal(t, "Anytown", claims["address"].(map[string]any)["locality"]) + assert.Equal(t, "Anystate", claims["address"].(map[string]any)["region"]) + assert.Equal(t, "US", claims["address"].(map[string]any)["country"]) + assert.Equal(t, "+1-202-555-0101", claims["phone_number"]) + assert.Equal(t, "johndoe@example.com", claims["email"]) + assert.Equal(t, "John", claims["given_name"]) + assert.Equal(t, "Doe", claims["family_name"]) + assert.Equal(t, "john_doe_42", claims["sub"]) + }, + }, + { + name: "valid token but duplicate disclosure", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFrb2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjogInVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + if err == nil { + t.Error("error should be thrown") + t.FailNow() + } + if sdJwt != nil { + t.Error("sdJwt should be nil: ", sdJwt) + } + if err.Error() != "failed to validate disclosures: duplicate disclosure found" { + t.Error("error message is not correct: ", err.Error()) + } + }, + }, + { + name: "valid token but xxx", + token: "eyJhbGciOiAiRVMyNTYifQ.eyJAY29udGV4dCI6IFsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCAiaHR0cHM6Ly93M2lkLm9yZy92YWNjaW5hdGlvbi92MSJdLCAidHlwZSI6IFsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCAiVmFjY2luYXRpb25DZXJ0aWZpY2F0ZSJdLCAiaXNzdWVyIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlzc3VhbmNlRGF0ZSI6ICIyMDIzLTAyLTA5VDExOjAxOjU5WiIsICJleHBpcmF0aW9uRGF0ZSI6ICIyMDI4LTAyLTA4VDExOjAxOjU5WiIsICJuYW1lIjogIkNPVklELTE5IFZhY2NpbmF0aW9uIENlcnRpZmljYXRlIiwgImRlc2NyaXB0aW9uIjogIkNPVklELTE5IFZhY2NpbmF0aW9uIENlcnRpZmljYXRlIiwgImNyZWRlbnRpYWxTdWJqZWN0IjogeyJfc2QiOiBbIjFWX0stOGxEUThpRlhCRlhiWlk5ZWhxUjRIYWJXQ2k1VDB5Ykl6WlBld3ciLCAiSnpqTGd0UDI5ZFAtQjN0ZDEyUDY3NGdGbUsyenk4MUhNdEJnZjZDSk5XZyIsICJSMmZHYmZBMDdaX1lsa3FtTlp5bWExeHl5eDFYc3RJaVM2QjFZYmwySlo0IiwgIlRDbXpybDdLMmdldl9kdTdwY01JeXpSTEhwLVllZy1GbF9jeHRyVXZQeGciLCAiVjdrSkJMSzc4VG1WRE9tcmZKN1p1VVBIdUtfMmNjN3laUmE0cVYxdHh3TSIsICJiMGVVc3ZHUC1PRERkRm9ZNE5semxYYzN0RHNsV0p0Q0pGNzVOdzhPal9nIiwgInpKS19lU01YandNOGRYbU1aTG5JOEZHTTA4ekozX3ViR2VFTUotNVRCeTAiXSwgInZhY2NpbmUiOiB7Il9zZCI6IFsiMWNGNWhMd2toTU5JYXFmV0pyWEk3Tk1XZWRMLTlmNlkyUEE1MnlQalNaSSIsICJIaXk2V1d1ZUxENWJuMTYyOTh0UHY3R1hobWxkTURPVG5CaS1DWmJwaE5vIiwgIkxiMDI3cTY5MWpYWGwtakM3M3ZpOGViT2o5c214M0MtX29nN2dBNFRCUUUiXSwgInR5cGUiOiAiVmFjY2luZSJ9LCAicmVjaXBpZW50IjogeyJfc2QiOiBbIjFsU1FCTlkyNHEwVGg2T0d6dGhxLTctNGw2Y0FheHJZWE9HWnBlV19sbkEiLCAiM256THE4MU0yb04wNndkdjFzaEh2T0VKVnhaNUtMbWREa0hFREpBQldFSSIsICJQbjFzV2kwNkc0TEpybm4tX1JUMFJiTV9IVGR4blBKUXVYMmZ6V3ZfSk9VIiwgImxGOXV6ZHN3N0hwbEdMYzcxNFRyNFdPN01HSnphN3R0N1FGbGVDWDRJdHciXSwgInR5cGUiOiAiVmFjY2luZVJlY2lwaWVudCJ9LCAidHlwZSI6ICJWYWNjaW5hdGlvbkV2ZW50In0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0.l7byWDsTtDOjFbWS4lko-3mkeeZwzUYw6ZicrJurES_gzs6EK_svPiVwj5g6evb_nmLWpK2_cXQ_J0cjH0XnGw~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImF0Y0NvZGUiLCAiSjA3QlgwMyJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgIm1lZGljaW5hbFByb2R1Y3ROYW1lIiwgIkNPVklELTE5IFZhY2NpbmUgTW9kZXJuYSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgIm1hcmtldGluZ0F1dGhvcml6YXRpb25Ib2xkZXIiLCAiTW9kZXJuYSBCaW90ZWNoIl0~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgIm5leHRWYWNjaW5hdGlvbkRhdGUiLCAiMjAyMS0wOC0xNlQxMzo0MDoxMloiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgImNvdW50cnlPZlZhY2NpbmF0aW9uIiwgIkdFIl0~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImRhdGVPZlZhY2NpbmF0aW9uIiwgIjIwMjEtMDYtMjNUMTM6NDA6MTJaIl0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgIm9yZGVyIiwgIjMvMyJd~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgImdlbmRlciIsICJGZW1hbGUiXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImJpcnRoRGF0ZSIsICIxOTYxLTA4LTE3Il0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgImdpdmVuTmFtZSIsICJNYXJpb24iXQ~WyI1YlBzMUlxdVpOYTBoa2FGenp6Wk53IiwgImZhbWlseU5hbWUiLCAiTXVzdGVybWFubiJd~WyI1YTJXMF9OcmxFWnpmcW1rXzdQcS13IiwgImFkbWluaXN0ZXJpbmdDZW50cmUiLCAiUHJheGlzIFNvbW1lcmdhcnRlbiJd~WyJ5MXNWVTV3ZGZKYWhWZGd3UGdTN1JRIiwgImJhdGNoTnVtYmVyIiwgIjE2MjYzODI3MzYiXQ~WyJIYlE0WDhzclZXM1FEeG5JSmRxeU9BIiwgImhlYWx0aFByb2Zlc3Npb25hbCIsICI4ODMxMTAwMDAwMTUzNzYiXQ~", + validate: func(t *testing.T, sdJwt *go_sd_jwt.SdJwt, err error) { + require.Nil(t, err, "must not error") + var disclosedClaims map[string]any + disclosedClaims, err = sdJwt.GetDisclosedClaims() + require.Nil(t, err, "must not error") + b, err := json.Marshal(disclosedClaims) + require.Nil(t, err, "must not error") + fmt.Println(string(b)) + }, + }, } - if err.Error() != "failed to validate disclosures: duplicate disclosure found" { - t.Error("error message is not correct: ", err.Error()) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sdJwt, err := go_sd_jwt.FromToken(tt.token, examplePublicKey) + tt.validate(t, sdJwt, err) + }) } } -func TestFromToken_DuplicateDigest(t *testing.T) { +func TestFromToken_AllDuplicateDigestScenarios(t *testing.T) { duplicateDigestSdClaimJwt := "eyJhbGciOiAiRVMyNTYifQ.ew0KICAiX3NkIjogWw0KICAgICJDclFlN1M1a3FCQUh0LW5NWVhnYzZiZHQyU0g1YVRZMXNVX00tUGdralBJIiwNCiAgICAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsDQogICAgIlBvckZicEt1VnU2eHltSmFndmtGc0ZYQWJSb2MySkdsQVVBMkJBNG83Y0kiLA0KICAgICJUR2Y0b0xiZ3dkNUpRYUh5S1ZRWlU5VWRHRTB3NXJ0RHNyWnpmVWFvbUxvIiwNCiAgICAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsDQogICAgIlh6RnJ6d3NjTTZHbjZDSkRjNnZWSzhCa01uZkc4dk9TS2ZwUElaZEFmZEUiLA0KICAgICJnYk9zSTRFZHEyeDJLdy13NXdQRXpha29iOWhWMWNSRDBBVE4zb1FMOUpNIiwNCiAgICAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCIsDQogICAgImpzdTl5VnVsd1FRbGhGbE1fM0psek1hU0Z6Z2xoUUcwRHBmYXlRd0xVSzQiDQogIF0sDQogICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLA0KICAiaWF0IjogMTY4MzAwMDAwMCwNCiAgImV4cCI6IDE4ODMwMDAwMDAsDQogICJzdWIiOiAidXNlcl80MiIsDQogICJuYXRpb25hbGl0aWVzIjogWw0KICAgIHsNCiAgICAgICIuLi4iOiAicEZuZGprWl9WQ3pteVRhNlVqbFpvM2RoLWtvOGFJS1FjOURsR3poYVZZbyINCiAgICB9LA0KICAgIHsNCiAgICAgICIuLi4iOiAiN0NmNkprUHVkcnkzbGNid0hnZVo4a2hBdjFVMU9TbGVyUDBWa0JKcldaMCINCiAgICB9DQogIF0sDQogICJfc2RfYWxnIjogInNoYS0yNTYiLA0KICAiY25mIjogew0KICAgICJqd2siOiB7DQogICAgICAia3R5IjogIkVDIiwNCiAgICAgICJjcnYiOiAiUC0yNTYiLA0KICAgICAgIngiOiAiVENBRVIxOVp2dTNPSEY0ajRXNHZmU1ZvSElQMUlMaWxEbHM3dkNlR2VtYyIsDQogICAgICAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIg0KICAgIH0NCiAgfQ0KfQ.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~" duplicateDigestArrayClaimJwt := "eyJhbGciOiAiRVMyNTYifQ.ew0KICAiX3NkIjogWw0KICAgICJDclFlN1M1a3FCQUh0LW5NWVhnYzZiZHQyU0g1YVRZMXNVX00tUGdralBJIiwNCiAgICAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsDQogICAgIlBvckZicEt1VnU2eHltSmFndmtGc0ZYQWJSb2MySkdsQVVBMkJBNG83Y0kiLA0KICAgICJUR2Y0b0xiZ3dkNUpRYUh5S1ZRWlU5VWRHRTB3NXJ0RHNyWnpmVWFvbUxvIiwNCiAgICAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsDQogICAgIlh6RnJ6d3NjTTZHbjZDSkRjNnZWSzhCa01uZkc4dk9TS2ZwUElaZEFmZEUiLA0KICAgICJnYk9zSTRFZHEyeDJLdy13NXdQRXpha29iOWhWMWNSRDBBVE4zb1FMOUpNIiwNCiAgICAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCINCiAgXSwNCiAgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsDQogICJpYXQiOiAxNjgzMDAwMDAwLA0KICAiZXhwIjogMTg4MzAwMDAwMCwNCiAgInN1YiI6ICJ1c2VyXzQyIiwNCiAgIm5hdGlvbmFsaXRpZXMiOiBbDQogICAgew0KICAgICAgIi4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIg0KICAgIH0sDQogICAgew0KICAgICAgIi4uLiI6ICI3Q2Y2SmtQdWRyeTNsY2J3SGdlWjhraEF2MVUxT1NsZXJQMFZrQkpyV1owIg0KICAgIH0sDQogICAgew0KICAgICAgIi4uLiI6ICI3Q2Y2SmtQdWRyeTNsY2J3SGdlWjhraEF2MVUxT1NsZXJQMFZrQkpyV1owIg0KICAgIH0NCiAgXSwNCiAgIl9zZF9hbGciOiAic2hhLTI1NiIsDQogICJjbmYiOiB7DQogICAgImp3ayI6IHsNCiAgICAgICJrdHkiOiAiRUMiLA0KICAgICAgImNydiI6ICJQLTI1NiIsDQogICAgICAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwNCiAgICAgICJ5IjogIlp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEiDQogICAgfQ0KICB9DQp9.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~" duplicateDigestSdArrayClaimJwt := "eyJhbGciOiAiRVMyNTYifQ.ew0KICAiX3NkIjogWw0KICAgICJDclFlN1M1a3FCQUh0LW5NWVhnYzZiZHQyU0g1YVRZMXNVX00tUGdralBJIiwNCiAgICAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsDQogICAgIlBvckZicEt1VnU2eHltSmFndmtGc0ZYQWJSb2MySkdsQVVBMkJBNG83Y0kiLA0KICAgICJUR2Y0b0xiZ3dkNUpRYUh5S1ZRWlU5VWRHRTB3NXJ0RHNyWnpmVWFvbUxvIiwNCiAgICAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsDQogICAgIlh6RnJ6d3NjTTZHbjZDSkRjNnZWSzhCa01uZkc4dk9TS2ZwUElaZEFmZEUiLA0KICAgICJnYk9zSTRFZHEyeDJLdy13NXdQRXpha29iOWhWMWNSRDBBVE4zb1FMOUpNIiwNCiAgICAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCINCiAgXSwNCiAgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsDQogICJpYXQiOiAxNjgzMDAwMDAwLA0KICAiZXhwIjogMTg4MzAwMDAwMCwNCiAgInN1YiI6ICJ1c2VyXzQyIiwNCiAgIm5hdGlvbmFsaXRpZXMiOiBbDQogICAgew0KICAgICAgIi4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIg0KICAgIH0sDQogICAgew0KICAgICAgIi4uLiI6ICI3Q2Y2SmtQdWRyeTNsY2J3SGdlWjhraEF2MVUxT1NsZXJQMFZrQkpyV1owIg0KICAgIH0sDQogICAgew0KICAgICAgIi4uLiI6ICJqc3U5eVZ1bHdRUWxoRmxNXzNKbHpNYVNGemdsaFFHMERwZmF5UXdMVUs0Ig0KICAgIH0NCiAgXSwNCiAgIl9zZF9hbGciOiAic2hhLTI1NiIsDQogICJjbmYiOiB7DQogICAgImp3ayI6IHsNCiAgICAgICJrdHkiOiAiRUMiLA0KICAgICAgImNydiI6ICJQLTI1NiIsDQogICAgICAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwNCiAgICAgICJ5IjogIlp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEiDQogICAgfQ0KICB9DQp9.kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJBSngtMDk1VlBycFR0TjRRTU9xUk9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJQYzMzSk0yTGNoY1VfbEhnZ3ZfdWZRIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0~WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0~" diff --git a/setters.go b/setters.go new file mode 100644 index 0000000..94455ac --- /dev/null +++ b/setters.go @@ -0,0 +1,21 @@ +package go_sd_jwt + +func (d *Disclosure) setClaimName(claimName *string) { + d.claimName = claimName +} + +func (d *Disclosure) setClaimValue(claimValue string) { + d.claimValue = claimValue +} + +func (d *Disclosure) setSalt(salt string) { + d.salt = salt +} + +func (d *Disclosure) setRawValue(rawValue string) { + d.rawValue = rawValue +} + +func (d *Disclosure) setEncodedValue(encodedValue string) { + d.encodedValue = encodedValue +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f8e7780 --- /dev/null +++ b/utils.go @@ -0,0 +1,16 @@ +package go_sd_jwt + +// Pointer is a helper method that returns a pointer to the given value. +func Pointer[T comparable](t T) *T { + return &t +} + +// PointerMap is a helper method that returns a pointer to the given map. +func PointerMap(m map[string]any) *map[string]any { + return &m +} + +// PointerSlice is a helper method that returns a pointer to the given slice. +func PointerSlice(s []any) *[]any { + return &s +}