Skip to content

Commit

Permalink
fix: x509 domain extraction and leaf certificate extraction (#1955)
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Auer <martin.auer97@gmail.com>
  • Loading branch information
auer-martin committed Jul 17, 2024
1 parent 1e9260a commit 72d2340
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
complexSdJwtVcPresentation,
contentChangedSdJwtVc,
expiredSdJwtVc,
funkeX509,
notBeforeInFutureSdJwtVc,
sdJwtVcWithSingleDisclosure,
sdJwtVcWithSingleDisclosurePresentation,
Expand All @@ -27,10 +28,11 @@ import {
} from './sdjwtvc.fixtures'

import {
CredoError,
Agent,
CredoError,
DidKey,
DidsModule,
getDomainFromUrl,
getJwkFromKey,
JwsService,
JwtPayload,
Expand Down Expand Up @@ -149,7 +151,7 @@ describe('SdJwtVcService', () => {
},
issuer: {
method: 'x5c',
x5c: [simpleX509.certificate],
x5c: [simpleX509.trustedCertficate],
issuer: 'some-issuer',
},
})
Expand All @@ -168,7 +170,7 @@ describe('SdJwtVcService', () => {
},
issuer: {
method: 'x5c',
x5c: [simpleX509.certificate],
x5c: [simpleX509.trustedCertficate],
issuer: simpleX509.certificateIssuer,
},
})
Expand All @@ -179,7 +181,7 @@ describe('SdJwtVcService', () => {
expect(sdJwtVc.header).toEqual({
typ: 'vc+sd-jwt',
alg: 'EdDSA',
x5c: [simpleX509.certificate],
x5c: [simpleX509.trustedCertficate],
})

expect(sdJwtVc.prettyClaims).toEqual({
Expand Down Expand Up @@ -797,7 +799,7 @@ describe('SdJwtVcService', () => {
})

const x509ModuleConfig = agent.context.dependencyManager.resolve(X509ModuleConfig)
await x509ModuleConfig.addTrustedCertificate(simpleX509.certificate)
await x509ModuleConfig.addTrustedCertificate(simpleX509.trustedCertficate)

const verificationResult = await sdJwtVcService.verify(agent.context, {
compactSdJwtVc: presentation,
Expand Down Expand Up @@ -846,6 +848,33 @@ describe('SdJwtVcService', () => {
})
})

test('Verify x509 chain protected sd-jwt-vc', async () => {
const x509ModuleConfig = agent.context.dependencyManager.resolve(X509ModuleConfig)
await x509ModuleConfig.addTrustedCertificate(funkeX509.trustedCertificate)

const verificationResult = await sdJwtVcService.verify(agent.context, {
compactSdJwtVc: funkeX509.sdJwtVc,
requiredClaimKeys: ['issuing_country'],
})

const sdJwtIss = verificationResult.sdJwtVc?.payload.iss
expect(sdJwtIss).toEqual('https://demo.pid-issuer.bundesdruckerei.de/c')
expect(getDomainFromUrl(sdJwtIss as string)).toEqual('demo.pid-issuer.bundesdruckerei.de')

expect(verificationResult).toEqual({
isValid: false,
error: new CredoError('JWT expired at 1718707804'),
sdJwtVc: expect.any(Object),
verification: {
isSignatureValid: true,
areRequiredClaimsIncluded: true,
isValid: false,
isValidJwtPayload: false,
isStatusValid: true,
},
})
})

test('Verify sd-jwt-vc with status where credential is not revoked', async () => {
const sdJwtVcService = agent.dependencyManager.resolve(SdJwtVcService)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ export const simpleJwtVc =
export const simpleX509 = {
sdJwtVc:
'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsIng1YyI6WyJNSUhlTUlHUm9BTUNBUUlDRUIwYW80ZVVBZUhrQjg2dzhmSUVuR2N3QlFZREsyVndNQUF3SGhjTk1qUXdOekUyTVRNek5URTNXaGNOTWpReE1ESXpNVEkwTlRNeVdqQUFNQ293QlFZREsyVndBeUVBMWMra1AwdFlodlN2LzJCdzdvSlFiQ1dZT2JUY0IyS1VPVHB3K0x0TG85dWpJVEFmTUIwR0ExVWRFUVFXTUJTR0VtaDBkSEJ6T2k4dmFYTnpkV1Z5TG1OdmJUQUZCZ01yWlhBRFFRQkU0SmFrbTh2bjI1NUI4ZEFneWdiaFIwWlBTZkNFbmdGdWlXREJkeUFYalc2YWhpdDZtOGlsZW05MDhreGsyeUpOZ2hUSVNCbERod2tmcmx5UFJ4NE0iXX0.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiaHR0cHM6Ly9pc3N1ZXIuY29tIiwiaWF0IjoxNjk4MTUxNTMyfQ.d254hz7u-mziOtFA3yA9tVNNDP5_6eJL-owg9prcr1jzVnYoiRyjPvzY7NuKuDqN5PeOaZ2x5GFYp7VwYX5RBw~',
certificate:
trustedCertficate:
'MIHeMIGRoAMCAQICEB0ao4eUAeHkB86w8fIEnGcwBQYDK2VwMAAwHhcNMjQwNzE2MTMzNTE3WhcNMjQxMDIzMTI0NTMyWjAAMCowBQYDK2VwAyEA1c+kP0tYhvSv/2Bw7oJQbCWYObTcB2KUOTpw+LtLo9ujITAfMB0GA1UdEQQWMBSGEmh0dHBzOi8vaXNzdWVyLmNvbTAFBgMrZXADQQBE4Jakm8vn255B8dAgygbhR0ZPSfCEngFuiWDBdyAXjW6ahit6m8ilem908kxk2yJNghTISBlDhwkfrlyPRx4M',
certificateIssuer: 'https://issuer.com',
}

export const funkeX509 = {
sdJwtVc:
'eyJ4NWMiOlsiTUlJQ2REQ0NBaHVnQXdJQkFnSUJBakFLQmdncWhrak9QUVFEQWpDQmlERUxNQWtHQTFVRUJoTUNSRVV4RHpBTkJnTlZCQWNNQmtKbGNteHBiakVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneEVUQVBCZ05WQkFzTUNGUWdRMU1nU1VSRk1UWXdOQVlEVlFRRERDMVRVRkpKVGtRZ1JuVnVhMlVnUlZWRVNTQlhZV3hzWlhRZ1VISnZkRzkwZVhCbElFbHpjM1ZwYm1jZ1EwRXdIaGNOTWpRd05UTXhNRGd4TXpFM1doY05NalV3TnpBMU1EZ3hNekUzV2pCc01Rc3dDUVlEVlFRR0V3SkVSVEVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneENqQUlCZ05WQkFzTUFVa3hNakF3QmdOVkJBTU1LVk5RVWtsT1JDQkdkVzVyWlNCRlZVUkpJRmRoYkd4bGRDQlFjbTkwYjNSNWNHVWdTWE56ZFdWeU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRU9GQnE0WU1LZzR3NWZUaWZzeXR3QnVKZi83RTdWaFJQWGlObTUyUzNxMUVUSWdCZFh5REsza1Z4R3hnZUhQaXZMUDN1dU12UzZpREVjN3FNeG12ZHVLT0JrRENCalRBZEJnTlZIUTRFRmdRVWlQaENrTEVyRFhQTFcyL0owV1ZlZ2h5dyttSXdEQVlEVlIwVEFRSC9CQUl3QURBT0JnTlZIUThCQWY4RUJBTUNCNEF3TFFZRFZSMFJCQ1l3SklJaVpHVnRieTV3YVdRdGFYTnpkV1Z5TG1KMWJtUmxjMlJ5ZFdOclpYSmxhUzVrWlRBZkJnTlZIU01FR0RBV2dCVFVWaGpBaVRqb0RsaUVHTWwyWXIrcnU4V1F2akFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUFiZjVUemtjUXpoZldvSW95aTFWTjdkOEk5QnNGS20xTVdsdVJwaDJieUdRSWdLWWtkck5mMnhYUGpWU2JqVy9VLzVTNXZBRUM1WHhjT2FudXNPQnJvQmJVPSIsIk1JSUNlVENDQWlDZ0F3SUJBZ0lVQjVFOVFWWnRtVVljRHRDaktCL0gzVlF2NzJnd0NnWUlLb1pJemowRUF3SXdnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUI0WERUSTBNRFV6TVRBMk5EZ3dPVm9YRFRNME1EVXlPVEEyTkRnd09Wb3dnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWUd6ZHdGRG5jNytLbjVpYkF2Q09NOGtlNzdWUXhxZk1jd1pMOElhSUErV0NST2NDZm1ZL2dpSDkycU1ydTVwL2t5T2l2RTBSQy9JYmRNT052RG9VeWFObU1HUXdIUVlEVlIwT0JCWUVGTlJXR01DSk9PZ09XSVFZeVhaaXY2dTd4WkMrTUI4R0ExVWRJd1FZTUJhQUZOUldHTUNKT09nT1dJUVl5WFppdjZ1N3haQytNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnR0dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR0VtN3drWktIdC9hdGI0TWRGblhXNnlybndNVVQydTEzNmdkdGwxMFk2aEFpQnVURnF2Vll0aDFyYnh6Q1AweFdaSG1RSzlrVnl4bjhHUGZYMjdFSXp6c3c9PSJdLCJraWQiOiJNSUdVTUlHT3BJR0xNSUdJTVFzd0NRWURWUVFHRXdKRVJURVBNQTBHQTFVRUJ3d0dRbVZ5YkdsdU1SMHdHd1lEVlFRS0RCUkNkVzVrWlhOa2NuVmphMlZ5WldrZ1IyMWlTREVSTUE4R0ExVUVDd3dJVkNCRFV5QkpSRVV4TmpBMEJnTlZCQU1NTFZOUVVrbE9SQ0JHZFc1clpTQkZWVVJKSUZkaGJHeGxkQ0JRY205MGIzUjVjR1VnU1hOemRXbHVaeUJEUVFJQkFnPT0iLCJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiZVNReDlVNFI4S2dKUnJISWZOYTdPeUhTR2I3WTh3bFNaWkc0MXVwWFN2ayJdfSwiX3NkIjpbIkdxZVRITW1mWFNyLVRpWHNscGVidTd2ay1aTzBnYkJ3N0ktbWtPR2k4UlEiLCIzMlpvemdIRl9kVFhRTVZMRWl6d0ZzelJ5b0IxVks5bHNnakFHaHlUdnBnIiwib2htY1pCTE1JUFRhOWN2cHVNdXRzcWRZdVFhaTRiR2RibmFJZDRSeDBQayIsIndQQ0lfbzQtbFhWVzkySkVCQkJPazUwUWVISGQtYWl4WGJzOWJ5dGhueWMiLCJqaTJ3VXd6RWJCWmFGOHRtLWRLaUZSVTZpbVp0bTU1ZGFOZkVaYW9xWTBzIiwidk5CS2RKNUVpakFuZWwxaVM4ZGFLS0J5TWd6d3JSelBzTU9vUEZTWkJQQSIsInlfeG5PUGVNU2M5Y2FjZ25VanBYYkFFeW9OZXVIZVpDYkdJdi1WYXU1YWsiXSwiYWRkcmVzcyI6eyJfc2QiOlsiWjhZbDBCalFaZGZ3LWZtbzRCZGR0SzVxMEM3Vk9OLXJ5ZjBUbEZwVUtZayIsIktWbUtzNnBCR0VsNHZTTTJrTF9RZ1FFTEdqLU5mR084blJGbkx0NWdlRlkiLCJabHJibS1BMHdMTURSVWQ0Z1o5SDRSUWJaR3BUMVpRNG5ZSGlxdFI2WU9NIl19LCJpc3N1aW5nX2NvdW50cnkiOiJERSIsInZjdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSIsImlzc3VpbmdfYXV0aG9yaXR5IjoiREUiLCJfc2RfYWxnIjoic2hhLTI1NiIsImlzcyI6Imh0dHBzOi8vZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZS9jIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlU2WDhQb3FpelozLVByTm93UlVRaDduLXR3bTlabzF3VTh0WDkxUEVtXzgiLCJ5IjoidGZjZHFnS3JVVTIyR2VFV0U3NDlMYThkSFlFTGJmSkdDWVFfYVFSM3BPcyJ9fSwiZXhwIjoxNzE4NzA3ODA0LCJpYXQiOjE3MTc0OTgyMDQsImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyJMOVN3ZS04cHUxZ1llS0ltdUNlQ1RKcTYwODQzeWlSeVV1Rlh0aUdSb1lzIiwiVWFWeWlOSlZzeFR1dHlJVG9rWXVsUDJ2cjVaaHEzRUVRMWNrZ1ptNHRHUSIsIlhGeWVmQ09vZHFJdkkyQm91Rl9hQVBVMF9tZDlaLUI3a1BvaXd4NTdYRk0iLCJsUXlnaFhaclY0dnRROUtwVVpKNnB6R0g5VlRIRUI5MDkzVjdjQmYySEQ4IiwiX2NzVjc3OEdSdEd1dlB1dlJ3Vi11SFhCeklvUTNPMEY0bVB1UHVlWGpYTSIsInptYm1xU2gwMTlNZzF3QVZpdWJyaWxNSW00UnpsTWhuc041Wi1kQkVIeWsiXX19.eX-vYUtV2FcJ2Qq8mlQybSlwmXjZ6jCzKdav-G5xR1mFOQFcLOWZ1j3dPRmdFCat3MWqsHbx6eaZjue4tmDF2Q~WyJKNm55NUdXVzlJZHNXQURKblNEQnN3IiwiZmFtaWx5X25hbWUiLCJNVVNURVJNQU5OIl0~WyJqcnVqMGV4RjdOVElGWEptOUJWQ1VnIiwiZ2l2ZW5fbmFtZSIsIkVSSUtBIl0~WyJRRkJBbnFIaUo3Y1A1MHBDak1IdUFBIiwiYmlydGhkYXRlIiwiMTk2NC0wOC0xMiJd~WyI1MmQxQVNmbmE3N1JERXE3bnR4ODFnIiwiYWdlX2JpcnRoX3llYXIiLDE5NjRd~WyJiZmRSdEl5NV9kRWloc3BRTmwzNDNBIiwiYWdlX2luX3llYXJzIiw1OV0~WyJXbG43VmxHTXhaXzRlSkc0dmNkYTRnIiwiYmlydGhfZmFtaWx5X25hbWUiLCJHQUJMRVIiXQ~WyJBUFdMWlRVOHRzS3NNWXBzazhtclVnIiwibmF0aW9uYWxpdGllcyIsWyJERSJdXQ~WyJOaE9LaFRNdEhxU2pzdmV1ekFLbWpBIiwiMTIiLHRydWVd~WyJFTlFGWURlTEV0bEo3cDhzdEFMSDJRIiwiMTQiLHRydWVd~WyJtNE5QUHFxY0VxVm4tWVNyQkFNMHVBIiwiMTYiLHRydWVd~WyJNT1R4eG1kUk05alhoX3dHbzZTQXpRIiwiMTgiLHRydWVd~WyJNZ3oySzRqTzFJVlZCU015RTNoZ1JRIiwiMjEiLHRydWVd~WyJNN0tjb0JIMjZPVzhPZUFicmZMakx3IiwiNjUiLGZhbHNlXQ~WyJRaW5JUERxdnJFem5Talp1MkZqOUl3IiwibG9jYWxpdHkiLCJCRVJMSU4iXQ~WyJxVFg0Si1URFBqTHh3TElhU3NZLWlBIiwibG9jYWxpdHkiLCJLw5ZMTiJd~WyJFSlNfdFlyVGI4VWVYWXJGY3pkNUd3IiwicG9zdGFsX2NvZGUiLCI1MTE0NyJd~WyJRZFhXN3BaWkVpV1ZLbXFZOGg1d0VBIiwic3RyZWV0X2FkZHJlc3MiLCJIRUlERVNUUkHhup5FIDE3Il0~',
trustedCertificate:
'MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf/7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2/J0WVeghyw+mIwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr+ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW/U/5S5vAEC5XxcOanusOBroBbU=',
}

export const expiredSdJwtVc =
'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJleHAiOjE3MTYxMTE5MTksImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyfQ.hOQ-CnT-iaL2_Dlui0NgVhBk2Lej4_AqDrEK-7bQNT2b6mJkaikvUXdNtg-z7GnCUNrjq35vm5ProqiyYQz_AA~'

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/modules/x509/X509Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class X509Service {
): X509Certificate {
if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')

const certificate = X509Certificate.fromEncodedCertificate(certificateChain[certificateChain.length - 1])
const certificate = X509Certificate.fromEncodedCertificate(certificateChain[0])

return certificate
}
Expand Down
35 changes: 5 additions & 30 deletions packages/core/src/utils/domain.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,9 @@
const endings = ['/', ':', '?', '#']
const starters = ['.', '/', '@']

/**
* Get domain from url
* https://github.com/bjarneo/extract-domain/blob/master/index.ts
*/
export function getDomainFromUrl(url: string): string {
let domainInc: number = 0
let offsetDomain: number = 0
let offsetStartSlice: number = 0
let offsetPath: number = 0
let len: number = url.length
let i: number = 0

while (len-- && ++i) {
if (domainInc && endings.indexOf(url[i]) > -1) break
if (url[i] !== '.') continue
++domainInc
offsetDomain = i
}

offsetPath = i
i = offsetDomain
while (i--) {
if (starters.indexOf(url[i]) === -1) continue
offsetStartSlice = i + 1
break
if (!url.startsWith('https://')) {
throw new Error('URL must start with "https://"')
}

if (offsetStartSlice === 0 && offsetPath > 3) return url
if (offsetStartSlice > 0 && offsetStartSlice < 2) return ''
return url.slice(offsetStartSlice, offsetPath)
const regex = /[#/?]/
const domain = url.substring('https://'.length).split(regex)[0]
return domain
}

0 comments on commit 72d2340

Please sign in to comment.