Skip to content

Commit

Permalink
Add identifier handling to ResourceLocator
Browse files Browse the repository at this point in the history
Introduces a new enum for identifier length and modifies the ResourceLocator class to include this identifier and its encoding/decoding logic. This enhances the resource locator functionality by supporting identifiers of varying lengths.
  • Loading branch information
pflynn-virtru committed Aug 15, 2024
1 parent 536c528 commit d4b53d0
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 21 deletions.
7 changes: 3 additions & 4 deletions lib/src/nanotdf/enum/ProtocolEnum.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
enum ProtocolEnum {
Http,
Https,
Unreserverd,
SharedResourceDirectory = 0xff,
Http = 0,
Https = 1,
SharedResourceDirectory = 0xf,
}

export default ProtocolEnum;
8 changes: 8 additions & 0 deletions lib/src/nanotdf/enum/ResourceLocatorIdentifierEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enum ResourceLocatorIdentifierEnum {
None = 0,
TwoBytes = 2,
EightBytes = 8,
ThirtyTwoBytes = 32,
}

export default ResourceLocatorIdentifierEnum;
98 changes: 81 additions & 17 deletions lib/src/nanotdf/models/ResourceLocator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ProtocolEnum from '../enum/ProtocolEnum.js';
import ResourceLocatorIdentifierEnum from '../enum/ResourceLocatorIdentifierEnum.js';

/**
*
Expand All @@ -10,6 +11,7 @@ import ProtocolEnum from '../enum/ProtocolEnum.js';
* | Protocol Enum | 1 | 1 |
* | Body Length | 1 | 1 |
* | Body | 1 | 255 |
* | Identifier | 0 | n |
*
* @link https://github.com/virtru/nanotdf/blob/master/spec/index.md#3312-kas
* @link https://github.com/virtru/nanotdf/blob/master/spec/index.md#341-resource-locator
Expand All @@ -18,6 +20,8 @@ export default class ResourceLocator {
readonly protocol: ProtocolEnum;
readonly lengthOfBody: number;
readonly body: string;
readonly identifier: string;
readonly identifierType: ResourceLocatorIdentifierEnum;
readonly offset: number = 0;

static readonly PROTOCOL_OFFSET = 0;
Expand All @@ -26,39 +30,86 @@ export default class ResourceLocator {
static readonly LENGTH_LENGTH = 1;
static readonly BODY_OFFSET = 2;

static parse(url: string): ResourceLocator {
static parse(url: string, identifier: string = ''): ResourceLocator {
const [protocol, body] = url.split('://');

// Buffer to hold the protocol, length of body, body
const buffer = new Uint8Array(1 + 1 + body.length);
buffer.set([body.length], 1);
buffer.set(new TextEncoder().encode(body), 2);

const bodyLength = body.length;
const identifierLength = identifier.length;
let identifierPaddedLength = 0;
// protocol and identifier byte
const protocolIdentifierByte = new Uint8Array(1);
if (protocol.toLowerCase() == 'http') {
buffer.set([ProtocolEnum.Http], 0);
protocolIdentifierByte[0] = protocolIdentifierByte[0] & 0x0F;
} else if (protocol.toLowerCase() == 'https') {
buffer.set([ProtocolEnum.Https], 0);
protocolIdentifierByte[0] = (protocolIdentifierByte[0] & 0x0F) | 0b0010;
} else {
throw new Error('Resource locator protocol is not supported.');
}

if (identifierLength === 0) {
protocolIdentifierByte[0] = (protocolIdentifierByte[0] & 0xF0) | 0b0000;
} else if (identifierLength <= 2) {
protocolIdentifierByte[0] = (protocolIdentifierByte[0] & 0xF0) | 0b0010;
identifierPaddedLength = ResourceLocatorIdentifierEnum.TwoBytes.valueOf();
} else if (identifierLength <= 8) {
protocolIdentifierByte[0] = (protocolIdentifierByte[0] & 0xF0) | 0b0100;
identifierPaddedLength = ResourceLocatorIdentifierEnum.EightBytes.valueOf();
} else if (identifierLength <= 32) {
protocolIdentifierByte[0] = (protocolIdentifierByte[0] & 0xF0) | 0b1000;
identifierPaddedLength = ResourceLocatorIdentifierEnum.ThirtyTwoBytes.valueOf();
} else {
throw new Error("Unsupported identifier length: " + identifierLength);
}
// Buffer to hold the protocol, length of body, body, and identifierPadded
const buffer = new Uint8Array(1 + 1 + bodyLength + identifierPaddedLength);
buffer.set(protocolIdentifierByte, 0);
buffer.set([bodyLength], 1);
buffer.set(new TextEncoder().encode(body), 2);
// add padded identifier
if (identifierPaddedLength > 0) {
const identifierArray = new Uint8Array(identifierPaddedLength);
const encodedIdentifier = new TextEncoder().encode(identifier).subarray(0, identifierPaddedLength);
identifierArray.set(encodedIdentifier);
buffer.set(identifierArray, 2 + bodyLength);
}
return new ResourceLocator(buffer);
}

constructor(buff: Uint8Array) {
// Protocol
this.protocol = buff[ResourceLocator.PROTOCOL_OFFSET];

// Length of body
this.lengthOfBody = buff[ResourceLocator.LENGTH_OFFSET];

// Body as utf8 string
const decoder = new TextDecoder();
this.body = decoder.decode(
buff.subarray(ResourceLocator.BODY_OFFSET, ResourceLocator.BODY_OFFSET + this.lengthOfBody)
);
// identifier
const identifierTypeNibble = this.protocol & 0xf;
if ((identifierTypeNibble & 0b0010) !== 0) {
this.identifierType = ResourceLocatorIdentifierEnum.TwoBytes;
} else if ((identifierTypeNibble & 0b0100) !== 0) {
this.identifierType = ResourceLocatorIdentifierEnum.EightBytes;
} else if ((identifierTypeNibble & 0b1000) !== 0) {
this.identifierType = ResourceLocatorIdentifierEnum.ThirtyTwoBytes;
}
switch (this.identifierType) {
case ResourceLocatorIdentifierEnum.None:
// noop
break;
case ResourceLocatorIdentifierEnum.TwoBytes:
case ResourceLocatorIdentifierEnum.EightBytes:
case ResourceLocatorIdentifierEnum.ThirtyTwoBytes:
this.identifier = decoder.decode(
// TODO test padding
buff.subarray(
ResourceLocator.BODY_OFFSET + this.lengthOfBody,
ResourceLocator.BODY_OFFSET + this.lengthOfBody + this.identifierType.valueOf()
)
);
break;
}
this.offset =
ResourceLocator.PROTOCOL_LENGTH + ResourceLocator.LENGTH_LENGTH + this.lengthOfBody;
ResourceLocator.PROTOCOL_LENGTH + ResourceLocator.LENGTH_LENGTH + this.lengthOfBody + this.identifierType.valueOf();
}

/**
Expand All @@ -73,7 +124,9 @@ export default class ResourceLocator {
// Length of the body( 1 byte)
1 +
// Content length
this.body.length
this.body.length +
// Identifier length
this.identifier.length
);
}

Expand All @@ -92,11 +145,13 @@ export default class ResourceLocator {
* Return the contents of the Resource Locator in buffer
*/
toBuffer(): Uint8Array {
const buffer = new Uint8Array(2 + this.body.length);
const buffer = new Uint8Array(2 + this.body.length + this.identifier.length);
buffer.set([this.protocol], 0);
buffer.set([this.lengthOfBody], 1);
buffer.set(new TextEncoder().encode(this.body), 2);

if (this.identifier) {
buffer.set(new TextEncoder().encode(this.identifier), 2 + this.body.length);
}
return buffer;
}

Expand All @@ -116,7 +171,16 @@ export default class ResourceLocator {
} else {
throw new Error(`Cannot create URL from protocol, "${ProtocolEnum[this.protocol]}"`);
}

return `${protocol}://${this.body}`;
}

/**
* Get Identifier
*
* Returns the identifier of the ResourceLocator or an empty string if no identifier is present.
* @returns { string } Identifier of the resource locator.
*/
getIdentifier(): string {
return this.identifier || '';
}
}

0 comments on commit d4b53d0

Please sign in to comment.