Skip to content

Commit

Permalink
Merge pull request #139 from TBD54566975/jiyoon/137-add-signerdid
Browse files Browse the repository at this point in the history
add `SignerDID` field to `jws.Decoded` struct
  • Loading branch information
Jiyoon Koo authored Apr 18, 2024
2 parents 5f11772 + ddf562d commit e1264a1
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 1 deletion.
13 changes: 12 additions & 1 deletion jws/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,21 @@ func Decode(jws string, opts ...DecodeOption) (Decoded, error) {
return Decoded{}, fmt.Errorf("malformed JWS. Failed to decode signature: %w", err)
}

if header.KID == "" {
return Decoded{}, errors.New("malformed JWS. Expected header to contain kid.")
}

signerDID, err := _did.Parse(header.KID)
if err != nil {
return Decoded{}, fmt.Errorf("malformed JWS. Failed to parse kid: %w", err)
}

return Decoded{
Header: header,
Payload: payload,
Signature: signature,
Parts: parts,
SignerDID: signerDID,
}, nil
}

Expand Down Expand Up @@ -218,12 +228,13 @@ func Verify(compactJWS string, opts ...DecodeOption) (Decoded, error) {
return decodedJWS, err
}

// Decoded is a compact JWS decoded into it's parts
// Decoded is a compact JWS decoded into its parts
type Decoded struct {
Header Header
Payload []byte
Signature []byte
Parts []string
SignerDID _did.DID
}

// Verify verifies the given compactJWS by resolving the DID Document from the kid header value
Expand Down
142 changes: 142 additions & 0 deletions jws/jws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,148 @@ func TestDecode(t *testing.T) {
assert.Equal(t, payload, decoded.Payload)
}

func TestDecode_SuccessWithTestJwtWithPayload(t *testing.T) {
jwsString := "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaU" +
"xDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbWRsWjI5YWNuWTVjemxuVWtwT1praFBlVGt5Tm" +
"1oa1drNTBVMWxZWjJoaFlsOVJSbWhGTlRNM1lrMGlmUSMwIiwidHlwIjoiSldUIn0" +
".eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0pqY25ZaU9pSkZaREkxTlRFNUlpd2ll" +
"Q0k2SW1kbFoyOWFjblk1Y3psblVrcE9aa2hQZVRreU5taGtXazUwVTFsWVoyaGhZbDlSUm1oRk5UTT" +
"NZazBpZlEiLCJqdGkiOiJ1cm46dmM6dXVpZDpjNWMzZGExMi02ODhmLTQxZDYtOTQzMC1lYzViNDAy" +
"NTFmMzMiLCJuYmYiOjE3MTE2NTA4MjcsInN1YiI6IjEyMyIsInZjIjp7IkBjb250ZXh0IjpbImh0dHB" +
"zOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZW" +
"RlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKamNuWWlPaUpGWkRJMU" +
"5URTVJaXdpZUNJNkltZGxaMjlhY25ZNWN6bG5Va3BPWmtoUGVUa3lObWhrV2s1MFUxbFlaMmhoWWw5UlJ" +
"taEZOVE0zWWswaWZRIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiIxMjMifSwiaWQiOiJ1cm46dmM" +
"6dXVpZDpjNWMzZGExMi02ODhmLTQxZDYtOTQzMC1lYzViNDAyNTFmMzMiLCJpc3N1YW5jZURhdGUiOiIy" +
"MDI0LTAzLTI4VDE4OjMzOjQ3WiJ9fQ" +
".ydUiwf33dDCdk4RyPfoTdgbK3yTUpLCDpPBIECbn-rCGn_W3q5QxzAt43ClOIWibpOXHs-9T86UDBFPyd79vAQ"

decoded, err := jws.Decode(jwsString)
assert.NoError(t, err)

assert.Equal(t, "EdDSA", decoded.Header.ALG)
assert.Equal(t, "JWT", decoded.Header.TYP)
assert.Equal(t, "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Imdl"+
"Z29acnY5czlnUkpOZkhPeTkyNmhkWk50U1lYZ2hhYl9RRmhFNTM3Yk0ifQ", decoded.SignerDID.URI)
var payloadMap map[string]interface{}

json.Unmarshal(decoded.Payload, &payloadMap)
if iss, ok := payloadMap["iss"].(string); ok {
assert.Equal(t, "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Imdl"+
"Z29acnY5czlnUkpOZkhPeTkyNmhkWk50U1lYZ2hhYl9RRmhFNTM3Yk0ifQ", iss)
} else {
t.Fail()
}
if subject, ok := payloadMap["sub"].(string); ok {
assert.Equal(t, "123", subject)
} else {
t.Fail()
}
if notBefore, ok := payloadMap["nbf"].(float64); ok {
assert.Equal(t, 1711650827, notBefore)
} else {
t.Fail()
}

}

func TestDecode_SuccessWithTestJwtWithDetachedPayload(t *testing.T) {
jwsString := "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaU" +
"xDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbWRsWjI5YWNuWTVjemxuVWtwT1praFBlVGt5Tm" +
"1oa1drNTBVMWxZWjJoaFlsOVJSbWhGTlRNM1lrMGlmUSMwIiwidHlwIjoiSldUIn0" +
"..ydUiwf33dDCdk4RyPfoTdgbK3yTUpLCDpPBIECbn-rCGn_W3q5QxzAt43ClOIWibpOXHs-9T86UDBFPyd79vAQ"

payloadBase64Url := "eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0pqY25ZaU9pSkZaREkxTlRFNUlpd2ll" +
"Q0k2SW1kbFoyOWFjblk1Y3psblVrcE9aa2hQZVRreU5taGtXazUwVTFsWVoyaGhZbDlSUm1oRk5UTT" +
"NZazBpZlEiLCJqdGkiOiJ1cm46dmM6dXVpZDpjNWMzZGExMi02ODhmLTQxZDYtOTQzMC1lYzViNDAy" +
"NTFmMzMiLCJuYmYiOjE3MTE2NTA4MjcsInN1YiI6IjEyMyIsInZjIjp7IkBjb250ZXh0IjpbImh0dHB" +
"zOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZW" +
"RlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKamNuWWlPaUpGWkRJMU" +
"5URTVJaXdpZUNJNkltZGxaMjlhY25ZNWN6bG5Va3BPWmtoUGVUa3lObWhrV2s1MFUxbFlaMmhoWWw5UlJ" +
"taEZOVE0zWWswaWZRIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiIxMjMifSwiaWQiOiJ1cm46dmM" +
"6dXVpZDpjNWMzZGExMi02ODhmLTQxZDYtOTQzMC1lYzViNDAyNTFmMzMiLCJpc3N1YW5jZURhdGUiOiIy" +
"MDI0LTAzLTI4VDE4OjMzOjQ3WiJ9fQ"

payloadByteArray, err := base64.StdEncoding.DecodeString(payloadBase64Url)
if err != nil {
fmt.Println("Error decoding base64 string:", err)
return
}

decoded, err := jws.Decode(jwsString, jws.Payload(payloadByteArray))
assert.NoError(t, err)

assert.Equal(t, "EdDSA", decoded.Header.ALG)
assert.Equal(t, "JWT", decoded.Header.TYP)
assert.Equal(t, "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Imdl"+
"Z29acnY5czlnUkpOZkhPeTkyNmhkWk50U1lYZ2hhYl9RRmhFNTM3Yk0ifQ", decoded.SignerDID.URI)
var payloadMap map[string]interface{}

json.Unmarshal(decoded.Payload, &payloadMap)
if iss, ok := payloadMap["iss"].(string); ok {
assert.Equal(t, "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Imdl"+
"Z29acnY5czlnUkpOZkhPeTkyNmhkWk50U1lYZ2hhYl9RRmhFNTM3Yk0ifQ", iss)
} else {
t.Fail()
}
if subject, ok := payloadMap["sub"].(string); ok {
assert.Equal(t, "123", subject)
} else {
t.Fail()
}
if notBefore, ok := payloadMap["nbf"].(float64); ok {
assert.Equal(t, 1711650827, notBefore)
} else {
t.Fail()
}

}

func TestDecode_HeaderIsNotBase64Url(t *testing.T) {

compactJWS := "lol." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." +
"SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

decoded, err := jws.Decode(compactJWS)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Failed to decode header")
assert.Equal(t, jws.Decoded{}, decoded)
}

func TestDecode_PayloadIsNotBase64Url(t *testing.T) {
compactJWS := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"{woohoo}." +
"SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

decoded, err := jws.Decode(compactJWS)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Failed to decode payload")
assert.Equal(t, jws.Decoded{}, decoded)
}

func TestDecode_SignatureIsNotBase64Url(t *testing.T) {
compactJWS := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." +
"{woot}"

decoded, err := jws.Decode(compactJWS)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Failed to decode signature")
assert.Equal(t, jws.Decoded{}, decoded)
}

func TestDecode_MissingHeaderKid(t *testing.T) {
compactJWS := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." +
"SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

decoded, err := jws.Decode(compactJWS)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Expected header to contain kid")
assert.Equal(t, jws.Decoded{}, decoded)
}

func TestDecode_Bad(t *testing.T) {
badHeader := base64.RawURLEncoding.EncodeToString([]byte("hehe"))
vectors := []string{
Expand Down

0 comments on commit e1264a1

Please sign in to comment.