Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into auth-code-flow-2
Browse files Browse the repository at this point in the history
  • Loading branch information
TimoGlastra committed Oct 9, 2024
2 parents 9ccb645 + 08a485b commit 8ab5b22
Show file tree
Hide file tree
Showing 71 changed files with 3,104 additions and 255 deletions.
5 changes: 0 additions & 5 deletions .changeset/fifty-tools-beam.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/popular-camels-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@credo-ts/openid4vc': patch
---

feat: add jarm-support
5 changes: 0 additions & 5 deletions .changeset/six-geese-pull.md

This file was deleted.

6 changes: 6 additions & 0 deletions .changeset/spotty-hounds-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@credo-ts/askar": patch
"@credo-ts/core": patch
---

feat: add direct ecdh-es jwe encryption/decryption
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<br />
<img
alt="Credo Logo"
src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png"
src="https://raw.githubusercontent.com/openwallet-foundation/credo-ts/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png"
height="250px"
/>
</p>
Expand Down
6 changes: 6 additions & 0 deletions packages/action-menu/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.5.11

### Patch Changes

- @credo-ts/core@0.5.11

## 0.5.10

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/action-menu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@credo-ts/action-menu",
"main": "src/index",
"types": "src/index",
"version": "0.5.10",
"version": "0.5.11",
"files": [
"build"
],
Expand Down
6 changes: 6 additions & 0 deletions packages/anoncreds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.5.11

### Patch Changes

- @credo-ts/core@0.5.11

## 0.5.10

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/anoncreds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@credo-ts/anoncreds",
"main": "src/index",
"types": "src/index",
"version": "0.5.10",
"version": "0.5.11",
"files": [
"build"
],
Expand All @@ -28,7 +28,7 @@
"dependencies": {
"@astronautlabs/jsonpath": "^1.1.2",
"@credo-ts/core": "workspace:*",
"@sphereon/pex-models": "^2.2.4",
"@sphereon/pex-models": "^2.3.1",
"big-integer": "^1.6.51",
"bn.js": "^5.2.1",
"class-transformer": "0.5.1",
Expand Down
6 changes: 6 additions & 0 deletions packages/askar/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.5.11

### Patch Changes

- @credo-ts/core@0.5.11

## 0.5.10

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/askar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@credo-ts/askar",
"main": "src/index",
"types": "src/index",
"version": "0.5.10",
"version": "0.5.11",
"files": [
"build"
],
Expand Down
3 changes: 2 additions & 1 deletion packages/askar/src/utils/askarKeyTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { KeyAlgs } from '@hyperledger/aries-askar-shared'
export enum AskarKeyTypePurpose {
KeyManagement = 'KeyManagement',
Signing = 'Signing',
Encryption = 'Encryption',
}

const keyTypeToAskarAlg = {
Expand All @@ -29,7 +30,7 @@ const keyTypeToAskarAlg = {
},
[KeyType.P256]: {
keyAlg: KeyAlgs.EcSecp256r1,
purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing],
purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing, AskarKeyTypePurpose.Encryption],
},
[KeyType.K256]: {
keyAlg: KeyAlgs.EcSecp256k1,
Expand Down
132 changes: 131 additions & 1 deletion packages/askar/src/wallet/AskarBaseWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
WalletExportImportConfig,
Logger,
SigningProviderRegistry,
WalletDirectEncryptCompactJwtEcdhEsOptions,
} from '@credo-ts/core'
import type { Session } from '@hyperledger/aries-askar-shared'

Expand All @@ -28,7 +29,15 @@ import {
KeyType,
utils,
} from '@credo-ts/core'
import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-shared'
import {
CryptoBox,
Store,
Key as AskarKey,
keyAlgFromString,
EcdhEs,
KeyAlgs,
Jwk,
} from '@hyperledger/aries-askar-shared'
import BigNumber from 'bn.js'

import { importSecureEnvironment } from '../secureEnvironment'
Expand Down Expand Up @@ -459,6 +468,127 @@ export abstract class AskarBaseWallet implements Wallet {
return returnValue
}

/**
* Method that enables JWE encryption using ECDH-ES and AesA256Gcm and returns it as a compact JWE.
* This method is specifically added to support OpenID4VP response encryption using JARM and should later be
* refactored into a more generic method that supports encryption/decryption.
*
* @returns compact JWE
*/
public async directEncryptCompactJweEcdhEs({
recipientKey,
encryptionAlgorithm,
apu,
apv,
data,
header,
}: WalletDirectEncryptCompactJwtEcdhEsOptions) {
if (encryptionAlgorithm !== 'A256GCM') {
throw new WalletError(`Encryption algorithm ${encryptionAlgorithm} is not supported. Only A256GCM is supported`)
}

// Only one supported for now
const encAlg = KeyAlgs.AesA256Gcm

// Create ephemeral key
const ephemeralKey = AskarKey.generate(keyAlgFromString(recipientKey.keyType))

const _header = {
...header,
apv,
apu,
enc: 'A256GCM',
alg: 'ECDH-ES',
epk: ephemeralKey.jwkPublic,
}

const encodedHeader = JsonEncoder.toBase64URL(_header)

const ecdh = new EcdhEs({
algId: Uint8Array.from(Buffer.from(encryptionAlgorithm)),
apu: apu ? Uint8Array.from(TypedArrayEncoder.fromBase64(apu)) : Uint8Array.from([]),
apv: apv ? Uint8Array.from(TypedArrayEncoder.fromBase64(apv)) : Uint8Array.from([]),
})

const { ciphertext, tag, nonce } = ecdh.encryptDirect({
encAlg,
ephemeralKey,
message: Uint8Array.from(data),
recipientKey: AskarKey.fromPublicBytes({
algorithm: keyAlgFromString(recipientKey.keyType),
publicKey: recipientKey.publicKey,
}),
// NOTE: aad is bytes of base64url encoded string. It SHOULD NOT be decoded as base64
aad: Uint8Array.from(Buffer.from(encodedHeader)),
})

const compactJwe = `${encodedHeader}..${TypedArrayEncoder.toBase64URL(nonce)}.${TypedArrayEncoder.toBase64URL(
ciphertext
)}.${TypedArrayEncoder.toBase64URL(tag)}`
return compactJwe
}

/**
* Method that enables JWE decryption using ECDH-ES and AesA256Gcm and returns it as plaintext buffer with the header.
* The apv and apu values are extracted from the heaader, and thus on a higher level it should be checked that these
* values are correct.
*/
public async directDecryptCompactJweEcdhEs({
compactJwe,
recipientKey,
}: {
compactJwe: string
recipientKey: Key
}): Promise<{ data: Buffer; header: Record<string, unknown> }> {
// encryption key is not used (we don't use key wrapping)
const [encodedHeader /* encryptionKey */, , encodedIv, encodedCiphertext, encodedTag] = compactJwe.split('.')

const header = JsonEncoder.fromBase64(encodedHeader)

if (header.alg !== 'ECDH-ES') {
throw new WalletError('Only ECDH-ES alg value is supported')
}
if (header.enc !== 'A256GCM') {
throw new WalletError('Only A256GCM enc value is supported')
}
if (!header.epk || typeof header.epk !== 'object') {
throw new WalletError('header epk value must contain a JWK')
}

// NOTE: we don't support custom key storage record at the moment.
let askarKey: AskarKey | null | undefined
if (isKeyTypeSupportedByAskarForPurpose(recipientKey.keyType, AskarKeyTypePurpose.KeyManagement)) {
askarKey = await this.withSession(
async (session) => (await session.fetchKey({ name: recipientKey.publicKeyBase58 }))?.key
)
}
if (!askarKey) {
throw new WalletError('Key entry not found')
}

// Only one supported for now
const encAlg = KeyAlgs.AesA256Gcm

const ecdh = new EcdhEs({
algId: Uint8Array.from(Buffer.from(header.enc)),
apu: header.apu ? Uint8Array.from(TypedArrayEncoder.fromBase64(header.apu)) : Uint8Array.from([]),
apv: header.apv ? Uint8Array.from(TypedArrayEncoder.fromBase64(header.apv)) : Uint8Array.from([]),
})

const plaintext = ecdh.decryptDirect({
nonce: TypedArrayEncoder.fromBase64(encodedIv),
ciphertext: TypedArrayEncoder.fromBase64(encodedCiphertext),
encAlg,
ephemeralKey: Jwk.fromJson(header.epk),
recipientKey: askarKey,
tag: TypedArrayEncoder.fromBase64(encodedTag),
// NOTE: aad is bytes of base64url encoded string. It SHOULD NOT be decoded as base64
aad: TypedArrayEncoder.fromString(encodedHeader),
})

return { data: Buffer.from(plaintext), header }
}

public async generateNonce(): Promise<string> {
try {
// generate an 80-bit nonce suitable for AnonCreds proofs
Expand Down
77 changes: 76 additions & 1 deletion packages/askar/src/wallet/__tests__/AskarWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
SignOptions,
VerifyOptions,
} from '@credo-ts/core'
import type { JwkProps } from '@hyperledger/aries-askar-shared'

import {
WalletKeyExistsError,
Expand All @@ -19,8 +20,12 @@ import {
TypedArrayEncoder,
KeyDerivationMethod,
Buffer,
JsonEncoder,
} from '@credo-ts/core'
import { Store } from '@hyperledger/aries-askar-shared'
import { Key as AskarKey } from '@hyperledger/aries-askar-nodejs'
import { Jwk, Store } from '@hyperledger/aries-askar-shared'
import { readFileSync } from 'fs'
import path from 'path'

import { KeyBackend } from '../../../../core/src/crypto/KeyBackend'
import { encodeToBase58 } from '../../../../core/src/utils/base58'
Expand Down Expand Up @@ -170,6 +175,76 @@ describe('AskarWallet basic operations', () => {
})
await expect(askarWallet.verify({ key: k256Key, data: message, signature })).resolves.toStrictEqual(true)
})

test('Encrypt and decrypt using JWE ECDH-ES', async () => {
const recipientKey = await askarWallet.createKey({
keyType: KeyType.P256,
})

const apv = TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromString('nonce-from-auth-request'))
const apu = TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromString(await askarWallet.generateNonce()))

const compactJwe = await askarWallet.directEncryptCompactJweEcdhEs({
data: JsonEncoder.toBuffer({ vp_token: ['something'] }),
apu,
apv,
encryptionAlgorithm: 'A256GCM',
header: {
kid: 'some-kid',
},
recipientKey,
})

const { data, header } = await askarWallet.directDecryptCompactJweEcdhEs({
compactJwe,
recipientKey,
})

expect(header).toEqual({
kid: 'some-kid',
apv,
apu,
enc: 'A256GCM',
alg: 'ECDH-ES',
epk: {
kty: 'EC',
crv: 'P-256',
x: expect.any(String),
y: expect.any(String),
},
})
expect(JsonEncoder.fromBuffer(data)).toEqual({ vp_token: ['something'] })
})

test('decrypt using JWE ECDH-ES based on test vector from OpenID Conformance test', async () => {
const {
compactJwe,
decodedPayload,
privateKeyJwk,
header: expectedHeader,
} = JSON.parse(
readFileSync(path.join(__dirname, '__fixtures__/jarm-jwe-encrypted-response.json')).toString('utf-8')
) as {
compactJwe: string
decodedPayload: Record<string, unknown>
privateKeyJwk: JwkProps
header: string
}

const key = AskarKey.fromJwk({ jwk: Jwk.fromJson(privateKeyJwk) })
const recipientKey = await askarWallet.createKey({
keyType: KeyType.P256,
privateKey: Buffer.from(key.secretBytes),
})

const { data, header } = await askarWallet.directDecryptCompactJweEcdhEs({
compactJwe,
recipientKey,
})

expect(header).toEqual(expectedHeader)
expect(JsonEncoder.fromBuffer(data)).toEqual(decodedPayload)
})
})

describe.skip('Currently, all KeyTypes are supported by Askar natively', () => {
Expand Down

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions packages/bbs-signatures/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.5.11

### Patch Changes

- @credo-ts/core@0.5.11

## 0.5.10

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/bbs-signatures/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@credo-ts/bbs-signatures",
"main": "src/index",
"types": "src/index",
"version": "0.5.10",
"version": "0.5.11",
"files": [
"build"
],
Expand Down
Loading

0 comments on commit 8ab5b22

Please sign in to comment.