From c45b39060fe8e6e5360e3b903f041fcbf956685d Mon Sep 17 00:00:00 2001 From: sivanov <sivanov.ctr@virtru.com> Date: Mon, 7 Aug 2023 13:57:24 +0300 Subject: [PATCH 1/2] draft --- lib/package.json | 2 +- lib/tdf3/src/client/index.ts | 4 ++++ lib/tdf3/src/tdf.ts | 21 ++++++++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/package.json b/lib/package.json index b2dcbf00..995d6b18 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "@opentdf/client", - "version": "1.2.1", + "version": "1.2.3", "description": "Access and generate tdf protected content", "homepage": "https://github.com/opentdf/client-web", "bugs": { diff --git a/lib/tdf3/src/client/index.ts b/lib/tdf3/src/client/index.ts index 53f5b9d2..9238b31d 100644 --- a/lib/tdf3/src/client/index.ts +++ b/lib/tdf3/src/client/index.ts @@ -370,6 +370,10 @@ export class Client { if (eo) { tdf.setEntity(eo); } + tdf.dpopEnabled = this.dpopEnabled; + if (sessionKeys.signingKeys) { + tdf.requestSignerKeyPair = sessionKeys.signingKeys; + } await tdf.addKeyAccess({ type: offline ? 'wrapped' : 'remote', url: this.kasEndpoint, diff --git a/lib/tdf3/src/tdf.ts b/lib/tdf3/src/tdf.ts index 9dbca0f4..e90a2339 100644 --- a/lib/tdf3/src/tdf.ts +++ b/lib/tdf3/src/tdf.ts @@ -3,9 +3,14 @@ import { EventEmitter } from 'events'; import axios from 'axios'; import crc32 from 'buffer-crc32'; import { v4 } from 'uuid'; -import { exportSPKI, importPKCS8, importX509 } from 'jose'; +import { exportSPKI, importPKCS8, importX509, KeyLike } from 'jose'; import { DecoratedReadableStream } from './client/DecoratedReadableStream.js'; import { EntityObject } from '../../src/tdf/EntityObject.js'; +import { + enums as cryptoEnums, +} from '../../src/nanotdf-crypto/index.js'; + +const { AlgorithmName } = cryptoEnums; import { AttributeSet, @@ -127,6 +132,7 @@ export class TDF extends EventEmitter { contentStream?: ReadableStream<Uint8Array>; manifest?: Manifest; entity?: EntityObject; + dpopEnabled?: Boolean; encryptionInformation?: SplitKey; htmlTransferUrl?: string; authProvider?: AuthProvider | AppIdAuthProvider; @@ -138,6 +144,7 @@ export class TDF extends EventEmitter { segmentSizeDefault: number; chunkMap: Map<string, Chunk>; cryptoService: CryptoService; + requestSignerKeyPair?: Required<Readonly<CryptoKeyPair>>; constructor(configuration: TDFConfiguration) { super(); @@ -520,7 +527,7 @@ export class TDF extends EventEmitter { //TODO I dont' think we need a body at all for KAS requests // Do we need ANY of this if it's already embedded in the EO in the Bearer OIDC token? - const body: Record<string, unknown> = { + let body: Record<string, unknown> = { keyAccess: keyAccessObject, policy: unsavedManifest.encryptionInformation.policy, entity: isAppIdProviderCheck(this.authProvider) ? this.entity : undefined, @@ -532,7 +539,15 @@ export class TDF extends EventEmitter { if (isAppIdProviderCheck(this.authProvider)) { body.authToken = await reqSignature({}, pkKeyLike); } else { - body.clientPayloadSignature = await reqSignature(body, pkKeyLike); + body.clientPublicKey = this.publicKey; + body = { + signedRequestToken: await reqSignature( + { requestBody: JSON.stringify(body)}, + (this.requestSignerKeyPair?.privateKey as KeyLike), + { alg: AlgorithmName.ES256, } + ) + } + // body.clientPayloadSignature = await reqSignature(body, pkKeyLike); } const httpReq = await this.authProvider.withCreds(this.buildRequest('POST', url, body)); From 0a27679634ee5cbb9e53798f9fee13fec07846e3 Mon Sep 17 00:00:00 2001 From: sivanov <sivanov.ctr@virtru.com> Date: Mon, 7 Aug 2023 17:42:56 +0300 Subject: [PATCH 2/2] should work but it doesnt --- lib/package.json | 4 +++- lib/src/nanotdf/Client.ts | 5 ++++- lib/tdf3/src/client/index.ts | 14 ++++++++++---- lib/tdf3/src/tdf.ts | 5 ++++- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/package.json b/lib/package.json index 995d6b18..8765d7fe 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "@opentdf/client", - "version": "1.2.3", + "version": "1.2.7", "description": "Access and generate tdf protected content", "homepage": "https://github.com/opentdf/client-web", "bugs": { @@ -10,6 +10,8 @@ "dist/*/src/**", "dist/*/tdf3/**", "dist/*/*.json", + "src/**", + "tdf3/**", "README.md" ], "repository": { diff --git a/lib/src/nanotdf/Client.ts b/lib/src/nanotdf/Client.ts index f8383aac..a54a4359 100644 --- a/lib/src/nanotdf/Client.ts +++ b/lib/src/nanotdf/Client.ts @@ -213,7 +213,10 @@ export default class Client { const jwtPayload = { requestBody: requestBodyStr }; const requestBody = { - signedRequestToken: await reqSignature(jwtPayload, this.requestSignerKeyPair.privateKey, { + signedRequestToken: await reqSignature( + jwtPayload, + this.requestSignerKeyPair.privateKey, + { alg: AlgorithmName.ES256, }), }; diff --git a/lib/tdf3/src/client/index.ts b/lib/tdf3/src/client/index.ts index 9238b31d..d7353039 100644 --- a/lib/tdf3/src/client/index.ts +++ b/lib/tdf3/src/client/index.ts @@ -14,6 +14,7 @@ import { OIDCRefreshTokenProvider } from '../../../src/auth/oidc-refreshtoken-pr import { OIDCExternalJwtProvider } from '../../../src/auth/oidc-externaljwt-provider.js'; import { CryptoService, PemKeyPair } from '../crypto/declarations.js'; import { AuthProvider, AppIdAuthProvider, HttpRequest } from '../../../src/auth/auth.js'; +import { generateKeyPair, enums as cryptoEnums, cryptoPublicToPem } from '../../../src/nanotdf-crypto/index.js'; import EAS from '../../../src/auth/Eas.js'; import { EncryptParams, DecryptParams, type Scope } from './builders.js'; @@ -28,7 +29,6 @@ import { import * as defaultCryptoService from '../crypto/index.js'; import { Policy } from '../models/index.js'; import { TdfError } from '../errors.js'; -import { rsaPkcs1Sha256 } from '../crypto/index.js'; const GLOBAL_BYTE_LIMIT = 64 * 1000 * 1000 * 1000; // 64 GB, see WS-9363. const HTML_BYTE_LIMIT = 100 * 1000 * 1000; // 100 MB, see WS-9476. @@ -137,7 +137,12 @@ export async function createSessionKeys({ let signingKeys; if (dpopEnabled) { - signingKeys = await crypto.subtle.generateKey(rsaPkcs1Sha256(), true, ['sign']); + signingKeys = await generateKeyPair({ + type: cryptoEnums.AlgorithmName.ECDSA, + curve: cryptoEnums.NamedCurve.P256, + keyUsages: [cryptoEnums.KeyUsageType.Sign, cryptoEnums.KeyUsageType.Verify], + isExtractable: true, + }); } // This will contact the auth server and forcibly refresh the auth token claims, @@ -145,8 +150,9 @@ export async function createSessionKeys({ // Note that we base64 encode the PEM string here as a quick workaround, simply because // a formatted raw PEM string isn't a valid header value and sending it raw makes keycloak's // header parser barf. There are more subtle ways to solve this, but this works for now. - if (authProvider && !isAppIdProviderCheck(authProvider)) { - await authProvider?.updateClientPublicKey(base64.encode(k2.publicKey), signingKeys); + if (authProvider && !isAppIdProviderCheck(authProvider) && signingKeys) { + const signerPubKey = await cryptoPublicToPem(signingKeys.publicKey); + await authProvider?.updateClientPublicKey(base64.encode(signerPubKey), signingKeys); } return { keypair: k2, signingKeys }; } diff --git a/lib/tdf3/src/tdf.ts b/lib/tdf3/src/tdf.ts index e90a2339..eba9d518 100644 --- a/lib/tdf3/src/tdf.ts +++ b/lib/tdf3/src/tdf.ts @@ -6,6 +6,8 @@ import { v4 } from 'uuid'; import { exportSPKI, importPKCS8, importX509, KeyLike } from 'jose'; import { DecoratedReadableStream } from './client/DecoratedReadableStream.js'; import { EntityObject } from '../../src/tdf/EntityObject.js'; +import DefaultParams from './../../src/nanotdf/models/DefaultParams.js'; + import { enums as cryptoEnums, } from '../../src/nanotdf-crypto/index.js'; @@ -528,6 +530,7 @@ export class TDF extends EventEmitter { //TODO I dont' think we need a body at all for KAS requests // Do we need ANY of this if it's already embedded in the EO in the Bearer OIDC token? let body: Record<string, unknown> = { + algorithm: DefaultParams.defaultECAlgorithm, keyAccess: keyAccessObject, policy: unsavedManifest.encryptionInformation.policy, entity: isAppIdProviderCheck(this.authProvider) ? this.entity : undefined, @@ -862,7 +865,7 @@ export class TDF extends EventEmitter { const url = `${keySplitInfo.url}/${isAppIdProvider ? '' : 'v2'}/rewrap`; const requestBodyStr = JSON.stringify({ - algorithm: 'RS256', + algorithm: DefaultParams.defaultECAlgorithm, keyAccess: keySplitInfo, policy: manifest.encryptionInformation.policy, clientPublicKey: this.publicKey,