-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
doc: Fully Expanded Passkey Objects + JSDoc types #6
Comments
Simple CBOR ParserExampleimport CBOR from './cbor.js';
let cborHex = 'bf2343ca3f7df297f41d21b452948cad10201d5ca9f590ef6600c4d0712cda301d00000000';
let cborBytes = Bytes.hexToBytes(cbor);
let data = CBOR.parse(cborBytes);
console.log(data); let CBOR = require('./cbor.js');
let cborHex = 'bf2343ca3f7df297f41d21b452948cad10201d5ca9f590ef6600c4d0712cda301d00000000';
let cborBytes = Bytes.hexToBytes(cbor);
let data = CBOR.parse(cborBytes);
console.log(data); Parser'use strict';
// jshint bitwise: false
/**
* @license CC0-1.0
*
* @root/cbor-parser.js - for WebAuthn Passkey data
*
* Authored in 2024 by AJ ONeal
* To the extent possible under law, the author(s) have dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along with
* this software. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
*/
// for ESM
let CBOR = {};
// for CommonJS:
//let CBOR = module.exports;
CBOR.parse = function (bytes) {
let cbor = CBOR.create(bytes);
let result = cbor._parse();
return result;
};
CBOR.create = function (data) {
let cbor = {};
cbor._data = new DataView(data.buffer, data.byteOffset, data.byteLength);
cbor._offset = 0;
cbor._parse = function () {
let initialByte = cbor._readByte();
let majorType = initialByte >> 5; // First 3 bits
let additionalInfo = initialByte & 0x1f; // Last 5 bits
return cbor._parseItem(majorType, additionalInfo);
};
cbor._parseItem = function (majorType, additionalInfo) {
switch (majorType) {
case 0:
return cbor._parseUnsignedInt(additionalInfo);
case 1:
return cbor._parseNegativeInt(additionalInfo);
case 2:
return cbor._parseByteString(additionalInfo);
case 3:
return cbor._parseTextString(additionalInfo);
case 4:
return cbor._parseArray(additionalInfo);
case 5:
return cbor._parseMap(additionalInfo);
case 6:
return cbor._parseTag(additionalInfo);
case 7:
return cbor._parseSimpleValue(additionalInfo);
default:
throw new Error(`Unsupported major type: ${majorType}`);
}
};
cbor._parseUnsignedInt = function (additionalInfo) {
return cbor._decodeLength(additionalInfo);
};
cbor._parseNegativeInt = function (additionalInfo) {
let unsignedValue = cbor._decodeLength(additionalInfo);
return -1 - unsignedValue;
};
cbor._parseByteString = function (additionalInfo) {
let length = cbor._decodeLength(additionalInfo);
return cbor._readBytes(length);
};
cbor._parseTextString = function (additionalInfo) {
let length = cbor._decodeLength(additionalInfo);
let textBytes = cbor._readBytes(length);
return new TextDecoder().decode(textBytes);
};
cbor._parseArray = function (additionalInfo) {
if (additionalInfo === 31) {
// Indefinite length array
let array = [];
while (cbor._peekByte() !== 0xff) {
array.push(cbor._parse());
}
cbor._readByte(); // consume stop code
return array;
}
let length = cbor._decodeLength(additionalInfo);
let array = [];
for (let i = 0; i < length; i += 1) {
array.push(cbor._parse());
}
return array;
};
cbor._parseMap = function (additionalInfo) {
if (additionalInfo === 31) {
// Indefinite length map
let map = {};
while (cbor._peekByte() !== 0xff) {
let key = cbor._parse();
let value = cbor._parse();
map[key] = value;
}
cbor._readByte(); // consume stop code
return map;
}
let length = cbor._decodeLength(additionalInfo);
let map = {};
for (let i = 0; i < length; i += 1) {
let key = cbor._parse();
let value = cbor._parse();
map[key] = value;
}
return map;
};
// Parse tags (e.g. for big numbers)
cbor._parseTag = function (additionalInfo) {
let tag = cbor._decodeLength(additionalInfo);
let value = cbor._parse();
if (tag === 18) {
// Special type for positive bignum
return value;
}
return { tag, value };
};
cbor._parseSimpleValue = function (additionalInfo) {
switch (additionalInfo) {
case 20:
return false;
case 21:
return true;
case 22:
return null;
case 23:
return undefined;
case 24:
return cbor._readByte(); // simple value
case 25:
return cbor._parseFloat16();
case 26:
return cbor._parseFloat32();
case 27:
return cbor._parseFloat64();
case 31:
return null; // Indefinite-length item (handled by array/map)
default:
throw new Error(`Unsupported simple value: ${additionalInfo}`);
}
};
cbor._decodeLength = function (additionalInfo) {
if (additionalInfo < 24) {
return additionalInfo;
}
if (additionalInfo === 24) {
return cbor._readByte();
}
if (additionalInfo === 25) {
return cbor._readUint16();
}
if (additionalInfo === 26) {
return cbor._readUint32();
}
if (additionalInfo === 27) {
return cbor._readUint64();
}
throw new Error(`Unsupported additional info: ${additionalInfo}`);
};
cbor._readUint16 = function () {
let val = cbor._data.getUint16(cbor._offset);
cbor._offset += 2;
return val;
};
cbor._readUint32 = function () {
let val = cbor._data.getUint32(cbor._offset);
cbor._offset += 4;
return val;
};
cbor._readUint64 = function () {
let high = cbor._data.getUint32(cbor._offset);
let low = cbor._data.getUint32(cbor._offset + 4);
cbor._offset += 8;
return (BigInt(high) << 32n) | BigInt(low);
};
cbor._readByte = function () {
let b = cbor._data.getUint8(cbor._offset);
cbor._offset += 1;
return b;
};
cbor._peekByte = function () {
return cbor._data.getUint8(cbor._offset);
};
cbor._readBytes = function (length) {
let bytes = new Uint8Array(cbor._data.buffer, cbor._offset, length);
cbor._offset += length;
return bytes;
};
cbor._parseFloat16 = function () {
let u16 = cbor._readUint16();
let exponentBits = u16 >> 10;
let exponent = exponentBits & 0x1f;
let mantissa = u16 & 0x3ff;
// Check for subnormal numbers (exponent = 0) or zero
if (exponent === 0) {
// Subnormal number or zero: exponent is implicitly -14, no leading 1 in mantissa
let f16 = mantissa * 5.96e-8; // 2^(-24)
return f16;
}
// Check for special cases (exponent = 31): infinity or NaN
if (exponent === 31) {
if (mantissa !== 0) {
return NaN;
}
return Infinity;
}
// Normalize the fraction (add the implicit leading 1 for normalized numbers)
let normalizedMantissa = mantissa / 1024 + 1;
// Adjust the exponent (bias of 15)
let adjustedExponent = exponent - 15;
// Calculate the result as: (1 + fraction) * 2^(exponent - bias)
let f16 = normalizedMantissa * 2 ** adjustedExponent;
return f16;
};
cbor._parseFloat32 = function () {
return cbor._data.getFloat32(cbor._offset, false);
};
cbor._parseFloat64 = function () {
let val = cbor._data.getFloat64(cbor._offset, false);
cbor._offset += 8;
return val;
};
return cbor;
};
// for ESM:
export default CBOR; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO:
Partial at go-webauthn/webauthn#291 (comment)
aaguid lookup: https://passkeydeveloper.github.io/passkey-authenticator-aaguids/explorer/?combined
Passkey (WebAuthn Credential) Registration (Creation)
./fixtures/apple-m2-01-credential-creation-response.json
(fully decoded):Passkey (WebAuthn Credential) Authentication (Attestation)
./fixtures/apple-m2-02-credential-request-response.json
(fully decoded):Code Examples
Registration
Authentication
The text was updated successfully, but these errors were encountered: