Skip to content

Commit

Permalink
hmm why is it not working in browsers with jose?
Browse files Browse the repository at this point in the history
  • Loading branch information
dmihalcik-virtru committed Feb 13, 2025
1 parent de14435 commit f4422b8
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 33 deletions.
9 changes: 9 additions & 0 deletions lib/src/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ export const keyAlgorithmToPublicKeyAlgorithm = (a: KeyAlgorithm): KasPublicKeyA
throw new Error(`unsupported key algorithm: ${a.name}`);
};

export const publicKeyAlgorithmToJwa = (a: KasPublicKeyAlgorithm): string => {
switch (a) {
case 'ec:secp256r1':
return 'ES256';
case 'rsa:2048':
return 'RS256';
}
};

/**
* Information about one of a KAS's published public keys.
* A KAS may publish multiple keys with a given algorithm type.
Expand Down
12 changes: 9 additions & 3 deletions lib/tdf3/src/models/key-access.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { base64, hex } from '../../../src/encodings/index.js';
import { generateKeyPair } from '../../../src/nanotdf-crypto/generateKeyPair.js';
import { generateRandomNumber } from '../../../src/nanotdf-crypto/generateRandomNumber.js';
import { keyAgreement } from '../../../src/nanotdf-crypto/keyAgreement.js';
import { pemPublicToCrypto } from '../../../src/nanotdf-crypto/pemPublicToCrypto.js';
Expand All @@ -18,7 +17,7 @@ export function isRemote(keyAccessJSON: KeyAccess | KeyAccessObject): boolean {

export class ECWrapped {
readonly type = 'ec-wrapped';
readonly ephemeralKeyPair;
readonly ephemeralKeyPair: Promise<CryptoKeyPair>;
keyAccessObject?: KeyAccessObject;

constructor(
Expand All @@ -28,7 +27,14 @@ export class ECWrapped {
public readonly metadata: unknown,
public readonly sid: string
) {
this.ephemeralKeyPair = generateKeyPair();
this.ephemeralKeyPair = crypto.subtle.generateKey(
{
name: 'ECDH',
namedCurve: 'P-256',
},
false,
['deriveBits', 'deriveKey']
);
}

async write(
Expand Down
56 changes: 32 additions & 24 deletions lib/tdf3/src/tdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
OriginAllowList,
fetchKasPubKey as fetchKasPubKeyV2,
fetchWrappedKey,
publicKeyAlgorithmToJwa,
} from '../../src/access.js';
import { type AuthProvider, reqSignature } from '../../src/auth/auth.js';
import { allPool, anyPool } from '../../src/concurrency.js';
Expand Down Expand Up @@ -80,6 +81,7 @@ export type Metadata = {

export type BuildKeyAccess = {
type: KeyAccessType;
alg?: KasPublicKeyAlgorithm;
url?: string;
kid?: string;
publicKey: string;
Expand Down Expand Up @@ -201,13 +203,17 @@ export async function fetchKasPublicKey(
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048');
}

export async function extractPemFromKeyString(keyString: string): Promise<string> {
export async function extractPemFromKeyString(
keyString: string,
alg: KasPublicKeyAlgorithm
): Promise<string> {
let pem: string = keyString;

// Skip the public key extraction if we find that the KAS url provides a
// PEM-encoded key instead of certificate
if (keyString.includes('CERTIFICATE')) {
const cert = await importX509(keyString, 'RS256', { extractable: true });
const a = publicKeyAlgorithmToJwa(alg);
const cert = await importX509(keyString, a, { extractable: true });
pem = await exportSPKI(cert);
}

Expand All @@ -234,32 +240,34 @@ export async function buildKeyAccess({
kid,
metadata,
sid = '',
alg = 'rsa:2048',
}: BuildKeyAccess): Promise<KeyAccess> {
/** Internal function to keep it DRY */
function createKeyAccess(
type: KeyAccessType,
kasUrl: string,
kasKeyIdentifier: string | undefined,
pubKey: string,
metadata?: Metadata
) {
switch (type) {
case 'wrapped':
return new Wrapped(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
case 'ec-wrapped':
return new ECWrapped(kasUrl, kasKeyIdentifier, pubKey, metadata, sid);
default:
throw new ConfigurationError(`buildKeyAccess: Key access type [${type}] is unsupported`);
}
}

// if url and pulicKey are specified load the key access object with them
if (url && publicKey) {
return createKeyAccess(type, url, kid, await extractPemFromKeyString(publicKey), metadata);
if (!url && !publicKey) {
throw new ConfigurationError('TDF.buildKeyAccess: No source for kasUrl or pubKey');
} else if (!url) {
throw new ConfigurationError('TDF.buildKeyAccess: No kasUrl');
} else if (!publicKey) {
throw new ConfigurationError('TDF.buildKeyAccess: No kas public key');
}

// All failed. Raise an error.
throw new ConfigurationError('TDF.buildKeyAccess: No source for kasUrl or pubKey');
let pubKey: string;
try {
pubKey = await extractPemFromKeyString(publicKey, alg);
} catch (e) {
throw new ConfigurationError(
`TDF.buildKeyAccess: Invalid public key [${publicKey}], caused by [${e}]`,
e
);
}
switch (type) {
case 'wrapped':
return new Wrapped(url, kid, pubKey, metadata, sid);
case 'ec-wrapped':
return new ECWrapped(url, kid, pubKey, metadata, sid);
default:
throw new ConfigurationError(`buildKeyAccess: Key access type [${type}] is unsupported`);
}
}

export function validatePolicyObject(policy: Policy): void {
Expand Down
9 changes: 6 additions & 3 deletions lib/tests/mocha/encrypt-decrypt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ describe('encrypt decrypt test', async function () {
authProvider,
});
const keyPair = await crypto.subtle.generateKey(
const assertionKeys = await crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
Expand All @@ -287,7 +288,7 @@ describe('encrypt decrypt test', async function () {
true,
['sign', 'verify']
);
const publicKey = keyPair.publicKey;
const assertionPublicKey = assertionKeys.publicKey;
const scope: Scope = {
dissem: ['[email protected]'],
attributes: [],
Expand All @@ -297,6 +298,8 @@ describe('encrypt decrypt test', async function () {
const hs256Key = new Uint8Array(32);
crypto.getRandomValues(hs256Key);

console.log('ASDF about to encrypt');

const encryptedStream = await client.encrypt({
metadata: Mocks.getMetadataObject(),
wrappingKeyAlgorithm: encapKeyType,
Expand Down Expand Up @@ -337,7 +340,7 @@ describe('encrypt decrypt test', async function () {
appliesToState: 'encrypted',
signingKey: {
alg: 'RS256',
key: keyPair.privateKey,
key: assertionKeys.privateKey,
},
},
{
Expand All @@ -364,7 +367,7 @@ describe('encrypt decrypt test', async function () {
},
assertion2: {
alg: 'RS256',
key: publicKey,
key: assertionPublicKey,
},
},
};
Expand Down
13 changes: 11 additions & 2 deletions lib/tests/mocha/unit/tdf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { KeyAccessObject } from '../../../tdf3/src/models/key-access.js';
import { PolicyBody, type Policy } from '../../../tdf3/src/models/policy.js';
import { OriginAllowList } from '../../../src/access.js';
import { ConfigurationError, InvalidFileError, UnsafeUrlError } from '../../../src/errors.js';
import { getMocks } from '../../mocks/index.js';

const sampleCert = `
-----BEGIN CERTIFICATE-----
Expand Down Expand Up @@ -42,9 +43,11 @@ HJg=
-----END CERTIFICATE-----
`.trim();

const { kasECCert } = getMocks();

describe('TDF', () => {
it('should return key', async () => {
const pem = await TDF.extractPemFromKeyString(sampleCert);
const pem = await TDF.extractPemFromKeyString(sampleCert, 'rsa:2048');
expect(pem).to.include('-----BEGIN PUBLIC KEY-----');
expect(pem).to.include('-----END PUBLIC KEY-----');
});
Expand All @@ -53,9 +56,15 @@ describe('TDF', () => {
const sampleKey = sampleCert
.replace('BEGIN CERTIFICATE', 'BEGIN PUBLIC KEY')
.replace('END CERTIFICATE', 'END PUBLIC KEY');
const pem = await TDF.extractPemFromKeyString(sampleKey);
const pem = await TDF.extractPemFromKeyString(sampleKey, 'rsa:2048');
expect(pem).to.equal(sampleKey);
});

it('should return ec pem', async () => {
const pem = await TDF.extractPemFromKeyString(kasECCert, 'ec:secp256r1');
expect(pem).to.include('-----BEGIN PUBLIC KEY-----');
expect(pem).to.include('-----END PUBLIC KEY-----');
});
});

describe('fetchKasPublicKey', async () => {
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ const kas: RequestListener = async (req, res) => {
namedCurve: 'P-256',
},
false,
['deriveKey']
['deriveBits', 'deriveKey']
);
const kek = await keyAgreement(sessionKeyPair.privateKey, clientPublicKey, {
hkdfSalt: new TextEncoder().encode('salt'),
Expand Down

0 comments on commit f4422b8

Please sign in to comment.