Skip to content

Commit

Permalink
wire authprovider to fetch kas keys
Browse files Browse the repository at this point in the history
  • Loading branch information
dmihalcik-virtru committed Dec 4, 2024
1 parent ae18fc2 commit b78cd0f
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 16 deletions.
26 changes: 16 additions & 10 deletions lib/src/access.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type AuthProvider } from './auth/auth.js';
import { type AuthProvider, type HttpRequest } from './auth/auth.js';
import {
ConfigurationError,
InvalidFileError,
Expand Down Expand Up @@ -125,13 +125,14 @@ async function noteInvalidPublicKey(url: URL, r: Promise<CryptoKey>): Promise<Cr
* If we have KAS url but not public key we can fetch it from KAS, fetching
* the value from `${kas}/kas_public_key`.
*/
export async function fetchECKasPubKey(kasEndpoint: string): Promise<KasPublicKeyInfo> {
return fetchKasPubKey(kasEndpoint, 'ec:secp256r1');
export async function fetchECKasPubKey(kasEndpoint: string, authProvider?: AuthProvider): Promise<KasPublicKeyInfo> {
return fetchKasPubKey(kasEndpoint, 'ec:secp256r1', authProvider);
}

export async function fetchKasPubKey(
kasEndpoint: string,
algorithm?: KasPublicKeyAlgorithm
algorithm?: KasPublicKeyAlgorithm,
authProvider?: AuthProvider,
): Promise<KasPublicKeyInfo> {
if (!kasEndpoint) {
throw new ConfigurationError('KAS definition not found');
Expand All @@ -146,23 +147,28 @@ export async function fetchKasPubKey(
}
pkUrlV2.searchParams.set('algorithm', infoStatic.algorithm);

let req: HttpRequest = {url: pkUrlV2.toString(), method: "GET", headers: {}};
if (authProvider) {
req = await authProvider.withCreds(req);
}

let kasPubKeyResponseV2: Response;
try {
kasPubKeyResponseV2 = await fetch(pkUrlV2);
kasPubKeyResponseV2 = await fetch(req.url, {method: req.method});
} catch (e) {
throw new NetworkError(`unable to fetch public key from [${pkUrlV2}]`, e);
throw new NetworkError(`unable to fetch public key from [${req.url}]`, e);
}
if (!kasPubKeyResponseV2.ok) {
switch (kasPubKeyResponseV2.status) {
case 404:
throw new ConfigurationError(`404 for [${pkUrlV2}]`);
throw new ConfigurationError(`404 for [${req.url}]`);
case 401:
throw new UnauthenticatedError(`401 for [${pkUrlV2}]`);
throw new UnauthenticatedError(`401 for [${req.url}]`);
case 403:
throw new PermissionDeniedError(`403 for [${pkUrlV2}]`);
throw new PermissionDeniedError(`403 for [${req.url}]`);
default:
throw new NetworkError(
`${pkUrlV2} => ${kasPubKeyResponseV2.status} ${kasPubKeyResponseV2.statusText}`
`${req.url} => ${kasPubKeyResponseV2.status} ${kasPubKeyResponseV2.statusText}`
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export class NanoTDFClient extends Client {
delete this.iv;

if (!this.kasPubKey) {
this.kasPubKey = await fetchECKasPubKey(this.kasUrl);
this.kasPubKey = await fetchECKasPubKey(this.kasUrl, this.authProvider);
}

// Create a policy for the tdf
Expand Down Expand Up @@ -273,7 +273,7 @@ export class NanoTDFDatasetClient extends Client {
const ephemeralKeyPair = await this.ephemeralKeyPair;

if (!this.kasPubKey) {
this.kasPubKey = await fetchECKasPubKey(this.kasUrl);
this.kasPubKey = await fetchECKasPubKey(this.kasUrl, this.authProvider);
}

// Create a policy for the tdf
Expand Down
2 changes: 1 addition & 1 deletion lib/tdf3/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ export class Client {
encryptionInformation.keyAccess = await Promise.all(
splits.map(async ({ kas, sid }) => {
if (!(kas in this.kasKeys)) {
this.kasKeys[kas] = fetchKasPublicKey(kas);
this.kasKeys[kas] = fetchKasPublicKey(kas, 'rsa:2048', this.authProvider);
}
const kasPublicKey = await this.kasKeys[kas];
return buildKeyAccess({
Expand Down
5 changes: 3 additions & 2 deletions lib/tdf3/src/tdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,10 @@ export type RewrapResponse = {
*/
export async function fetchKasPublicKey(
kas: string,
algorithm?: KasPublicKeyAlgorithm
algorithm?: KasPublicKeyAlgorithm,
authProvider?: AuthProvider,
): Promise<KasPublicKeyInfo> {
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048');
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048', authProvider);
}

/**
Expand Down
14 changes: 13 additions & 1 deletion lib/tests/mocha/unit/tdf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from 'chai';
import * as TDF from '../../../tdf3/src/tdf.js';
import { KeyAccessObject } from '../../../tdf3/src/models/key-access.js';
import { OriginAllowList } from '../../../src/access.js';
import { InvalidFileError, TdfError, UnsafeUrlError } from '../../../src/errors.js';
import { InvalidFileError, NetworkError, TdfError, UnsafeUrlError } from '../../../src/errors.js';

const sampleCert = `
-----BEGIN CERTIFICATE-----
Expand Down Expand Up @@ -89,6 +89,18 @@ describe('fetchKasPublicKey', async () => {
expect(pk2.publicKey).to.include('BEGIN CERTIFICATE');
expect(pk2.kid).to.equal('r1');
});

it('fails on invalid auth', async () => {
try {
await TDF.fetchKasPublicKey('http://localhost:3000', 'rsa:2048', {
withCreds: async () => { throw new NetworkError('no creds') },
updateClientPublicKey: async () => { },
});
expect.fail('did not throw');
} catch (e) {
expect(() => {throw e}).to.throw(NetworkError, 'no creds');
}
});
});

describe('splitLookupTableFactory', () => {
Expand Down

0 comments on commit b78cd0f

Please sign in to comment.