@ideem/zsm-client-sdk
Version:
ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no relian
62 lines (51 loc) • 6.74 kB
JavaScript
/**
@name decodeAttestationObject
@description Decodes a CBOR-encoded attestation object buffer into a JavaScript object (CBOR map of 3 entries (0xA3)) with properties 'fmt', 'attStmt', and 'authData'.
@param {ArrayBuffer} buffer The CBOR-encoded attestation object.
@returns {Object} The decoded attestation object with properties 'fmt', 'attStmt', and 'authData'.
@throws {Error} If the first byte does not indicate a CBOR map of 3 entries.
@throws {Error} If the major type of a value is unsupported.
@throws {Error} If the input buffer is not a valid CBOR-encoded attestation object.
*/
function decodeAttestationObject(buffer) {
try {
if (!buffer) // IF the buffer argument is missing...
throw new TypeError("Invalid input"); // ...throw an error
if (buffer instanceof Uint8Array) // IF the buffer argument is a Uint8Array...
buffer = buffer.buffer; // ...convert it to an ArrayBuffer
if (!(buffer instanceof ArrayBuffer)) // IF the buffer argument is (STILL) not an ArrayBuffer...
throw new TypeError("Invalid input"); // ...throw an error
if (!buffer || !(buffer instanceof ArrayBuffer)) // IF the buffer argument is missing or not an ArrayBuffer...
throw new TypeError("Invalid input"); // ...throw an error
const view = new DataView(buffer); // Create a DataView to read the buffer argument (CBOR is a binary format)
const attObj = {}; // Create an object to hold the decoded attestation object
let offset = 0; // Create an offset counter to track our position in the buffer
// Expect a CBOR map of 3 entries (0xA3)
const type = view.getUint8(offset++); // Read the first byte to determine the major type
if (type !== 0xA3) throw new Error("Unexpected format"); // Since we expect a map with 3 key-value pairs, throw an error if not found
for (let i = 0; i < 3; i++) { // Iterate over the 3 key-value pairs...
const keyLen = view.getUint8(offset++) - 0x60; // Read the length of the key (CBOR short string)
const key = new TextDecoder() // Isolate out the key by initializing a TextDecoder...
.decode(new Uint8Array(buffer, offset, keyLen)); // ...and decoding the buffered CBOR short string into it
offset += keyLen; // Move offset past the key by the length of the key
const majorType = view.getUint8(offset); // Read the major type of the value
offset++; // Move past the major type byte
if (majorType === 0x58) { // IF the major type's value is a byte string with length in next byte...
const len = view.getUint8(offset++); // ...read the length of the byte string...
attObj[key] = new Uint8Array(buffer, offset, len); // ...extract the byte string...
offset += len; // ...and increment offset by length.
} else if (majorType >= 0x60 && majorType < 0x78) { // OTHERWISE, if major type's value is a short string (for 'fmt')
const len = majorType - 0x60; // ...determine length from major type...
attObj[key] = new TextDecoder() // ...and initialize a TextDecoder...
.decode(new Uint8Array(buffer, offset, len)); // ...decoding the buffered CBOR short string into it
offset += len; // ...finally, increment offset by length
} else if (majorType === 0xA0) { // OTHERWISE, if major type's value is an empty map
attObj[key] = {}; // ...and assign an empty object
} else throw new Error("Unsupported type"); // OTHERWISE, throw an error
}
return attObj; // return the decoded attestation object
} catch (e) {
throw new Error("[Utils] :: decodeAttestationObject :: Failed to decode attestation object: " + e.message);
}
}
export { decodeAttestationObject };