Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@simplewebauthn/server

Package Overview
Dependencies
Maintainers
1
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@simplewebauthn/server - npm Package Compare versions

Comparing version 10.0.0 to 10.0.1

3

esm/helpers/iso/isoCrypto/unwrapEC2Signature.d.ts

@@ -0,1 +1,2 @@

import { COSECRV } from '../../cose.js';
/**

@@ -6,2 +7,2 @@ * In WebAuthn, EC2 signatures are wrapped in ASN.1 structure so we need to peel r and s apart.

*/
export declare function unwrapEC2Signature(signature: Uint8Array): Uint8Array;
export declare function unwrapEC2Signature(signature: Uint8Array, crv: COSECRV): Uint8Array;
import { AsnParser, ECDSASigValue } from '../../../deps.js';
import { COSECRV } from '../../cose.js';
import { isoUint8Array } from '../index.js';

@@ -8,24 +9,65 @@ /**

*/
export function unwrapEC2Signature(signature) {
export function unwrapEC2Signature(signature, crv) {
const parsedSignature = AsnParser.parse(signature, ECDSASigValue);
let rBytes = new Uint8Array(parsedSignature.r);
let sBytes = new Uint8Array(parsedSignature.s);
if (shouldRemoveLeadingZero(rBytes)) {
rBytes = rBytes.slice(1);
}
if (shouldRemoveLeadingZero(sBytes)) {
sBytes = sBytes.slice(1);
}
const finalSignature = isoUint8Array.concat([rBytes, sBytes]);
const rBytes = new Uint8Array(parsedSignature.r);
const sBytes = new Uint8Array(parsedSignature.s);
const componentLength = getSignatureComponentLength(crv);
const rNormalizedBytes = toNormalizedBytes(rBytes, componentLength);
const sNormalizedBytes = toNormalizedBytes(sBytes, componentLength);
const finalSignature = isoUint8Array.concat([
rNormalizedBytes,
sNormalizedBytes,
]);
return finalSignature;
}
/**
* Determine if the DER-specific `00` byte at the start of an ECDSA signature byte sequence
* should be removed based on the following logic:
* The SubtleCrypto Web Crypto API expects ECDSA signatures with `r` and `s` values to be encoded
* to a specific length depending on the order of the curve. This function returns the expected
* byte-length for each of the `r` and `s` signature components.
*
* "If the leading byte is 0x0, and the the high order bit on the second byte is not set to 0,
* then remove the leading 0x0 byte"
* See <https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations>
*/
function shouldRemoveLeadingZero(bytes) {
return bytes[0] === 0x0 && (bytes[1] & (1 << 7)) !== 0;
function getSignatureComponentLength(crv) {
switch (crv) {
case COSECRV.P256:
return 32;
case COSECRV.P384:
return 48;
case COSECRV.P521:
return 66;
default:
throw new Error(`Unexpected COSE crv value of ${crv} (EC2)`);
}
}
/**
* Converts the ASN.1 integer representation to bytes of a specific length `n`.
*
* DER encodes integers as big-endian byte arrays, with as small as possible representation and
* requires a leading `0` byte to disambiguate between negative and positive numbers. This means
* that `r` and `s` can potentially not be the expected byte-length that is needed by the
* SubtleCrypto Web Crypto API: if there are leading `0`s it can be shorter than expected, and if
* it has a leading `1` bit, it can be one byte longer.
*
* See <https://www.itu.int/rec/T-REC-X.690-202102-I/en>
* See <https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations>
*/
function toNormalizedBytes(bytes, componentLength) {
let normalizedBytes;
if (bytes.length < componentLength) {
// In case the bytes are shorter than expected, we need to pad it with leading `0`s.
normalizedBytes = new Uint8Array(componentLength);
normalizedBytes.set(bytes, componentLength - bytes.length);
}
else if (bytes.length === componentLength) {
normalizedBytes = bytes;
}
else if (bytes.length === componentLength + 1 && bytes[0] === 0 && (bytes[1] & 0x80) === 0x80) {
// The bytes contain a leading `0` to encode that the integer is positive. This leading `0`
// needs to be removed for compatibility with the SubtleCrypto Web Crypto API.
normalizedBytes = bytes.subarray(1);
}
else {
throw new Error(`Invalid signature component length ${bytes.length}, expected ${componentLength}`);
}
return normalizedBytes;
}

@@ -1,2 +0,2 @@

import { COSEKEYS, isCOSEPublicKeyEC2, isCOSEPublicKeyOKP, isCOSEPublicKeyRSA, } from '../../cose.js';
import { COSEKEYS, isCOSECrv, isCOSEPublicKeyEC2, isCOSEPublicKeyOKP, isCOSEPublicKeyRSA, } from '../../cose.js';
import { verifyEC2 } from './verifyEC2.js';

@@ -12,3 +12,7 @@ import { verifyRSA } from './verifyRSA.js';

if (isCOSEPublicKeyEC2(cosePublicKey)) {
const unwrappedSignature = unwrapEC2Signature(signature);
const crv = cosePublicKey.get(COSEKEYS.crv);
if (!isCOSECrv(crv)) {
throw new Error(`unknown COSE curve ${crv}`);
}
const unwrappedSignature = unwrapEC2Signature(signature, crv);
return verifyEC2({

@@ -15,0 +19,0 @@ cosePublicKey,

@@ -5,3 +5,3 @@ {

"name": "@simplewebauthn/server",
"version": "10.0.0",
"version": "10.0.1",
"description": "SimpleWebAuthn for Servers",

@@ -8,0 +8,0 @@ "license": "MIT",

@@ -0,1 +1,2 @@

import { COSECRV } from '../../cose.js';
/**

@@ -6,2 +7,2 @@ * In WebAuthn, EC2 signatures are wrapped in ASN.1 structure so we need to peel r and s apart.

*/
export declare function unwrapEC2Signature(signature: Uint8Array): Uint8Array;
export declare function unwrapEC2Signature(signature: Uint8Array, crv: COSECRV): Uint8Array;

@@ -5,2 +5,3 @@ "use strict";

const deps_js_1 = require("../../../deps.js");
const cose_js_1 = require("../../cose.js");
const index_js_1 = require("../index.js");

@@ -12,13 +13,13 @@ /**

*/
function unwrapEC2Signature(signature) {
function unwrapEC2Signature(signature, crv) {
const parsedSignature = deps_js_1.AsnParser.parse(signature, deps_js_1.ECDSASigValue);
let rBytes = new Uint8Array(parsedSignature.r);
let sBytes = new Uint8Array(parsedSignature.s);
if (shouldRemoveLeadingZero(rBytes)) {
rBytes = rBytes.slice(1);
}
if (shouldRemoveLeadingZero(sBytes)) {
sBytes = sBytes.slice(1);
}
const finalSignature = index_js_1.isoUint8Array.concat([rBytes, sBytes]);
const rBytes = new Uint8Array(parsedSignature.r);
const sBytes = new Uint8Array(parsedSignature.s);
const componentLength = getSignatureComponentLength(crv);
const rNormalizedBytes = toNormalizedBytes(rBytes, componentLength);
const sNormalizedBytes = toNormalizedBytes(sBytes, componentLength);
const finalSignature = index_js_1.isoUint8Array.concat([
rNormalizedBytes,
sNormalizedBytes,
]);
return finalSignature;

@@ -28,10 +29,51 @@ }

/**
* Determine if the DER-specific `00` byte at the start of an ECDSA signature byte sequence
* should be removed based on the following logic:
* The SubtleCrypto Web Crypto API expects ECDSA signatures with `r` and `s` values to be encoded
* to a specific length depending on the order of the curve. This function returns the expected
* byte-length for each of the `r` and `s` signature components.
*
* "If the leading byte is 0x0, and the the high order bit on the second byte is not set to 0,
* then remove the leading 0x0 byte"
* See <https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations>
*/
function shouldRemoveLeadingZero(bytes) {
return bytes[0] === 0x0 && (bytes[1] & (1 << 7)) !== 0;
function getSignatureComponentLength(crv) {
switch (crv) {
case cose_js_1.COSECRV.P256:
return 32;
case cose_js_1.COSECRV.P384:
return 48;
case cose_js_1.COSECRV.P521:
return 66;
default:
throw new Error(`Unexpected COSE crv value of ${crv} (EC2)`);
}
}
/**
* Converts the ASN.1 integer representation to bytes of a specific length `n`.
*
* DER encodes integers as big-endian byte arrays, with as small as possible representation and
* requires a leading `0` byte to disambiguate between negative and positive numbers. This means
* that `r` and `s` can potentially not be the expected byte-length that is needed by the
* SubtleCrypto Web Crypto API: if there are leading `0`s it can be shorter than expected, and if
* it has a leading `1` bit, it can be one byte longer.
*
* See <https://www.itu.int/rec/T-REC-X.690-202102-I/en>
* See <https://www.w3.org/TR/WebCryptoAPI/#ecdsa-operations>
*/
function toNormalizedBytes(bytes, componentLength) {
let normalizedBytes;
if (bytes.length < componentLength) {
// In case the bytes are shorter than expected, we need to pad it with leading `0`s.
normalizedBytes = new Uint8Array(componentLength);
normalizedBytes.set(bytes, componentLength - bytes.length);
}
else if (bytes.length === componentLength) {
normalizedBytes = bytes;
}
else if (bytes.length === componentLength + 1 && bytes[0] === 0 && (bytes[1] & 0x80) === 0x80) {
// The bytes contain a leading `0` to encode that the integer is positive. This leading `0`
// needs to be removed for compatibility with the SubtleCrypto Web Crypto API.
normalizedBytes = bytes.subarray(1);
}
else {
throw new Error(`Invalid signature component length ${bytes.length}, expected ${componentLength}`);
}
return normalizedBytes;
}

@@ -15,3 +15,7 @@ "use strict";

if ((0, cose_js_1.isCOSEPublicKeyEC2)(cosePublicKey)) {
const unwrappedSignature = (0, unwrapEC2Signature_js_1.unwrapEC2Signature)(signature);
const crv = cosePublicKey.get(cose_js_1.COSEKEYS.crv);
if (!(0, cose_js_1.isCOSECrv)(crv)) {
throw new Error(`unknown COSE curve ${crv}`);
}
const unwrappedSignature = (0, unwrapEC2Signature_js_1.unwrapEC2Signature)(signature, crv);
return (0, verifyEC2_js_1.verifyEC2)({

@@ -18,0 +22,0 @@ cosePublicKey,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc