Skip to content

Commit

Permalink
Merge pull request #8 from s1r-J/develop
Browse files Browse the repository at this point in the history
Bump for v0.2.1
  • Loading branch information
s1r-J authored Oct 15, 2022
2 parents 4fe1a6d + 773d0f6 commit 9c92ff0
Show file tree
Hide file tree
Showing 22 changed files with 5,232 additions and 444 deletions.
5,315 changes: 5,042 additions & 273 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 24 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@s1r-j/fido2server-lib",
"version": "0.2.0",
"version": "0.2.1",
"description": "Module to help implement FIDO2 server.",
"main": "index.js",
"types": "./dist/index.d.ts",
Expand All @@ -9,15 +9,17 @@
"dist/*"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prebuild": "rm -rf dist/*",
"build": "npm run build:dist",
"build:dist": "tsc -p tsconfig.dist.json && tsc-alias -p tsconfig.dist.json",
"clean": "rm -rf dist/*",
"test": "tap __test__/**/*.test.ts --ts --no-check-coverage --timeout=60",
"prebuild": "npm run clean",
"build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
"prepublishOnly": "npm run build",
"check-types": "tsc -p tsconfig.eslint.json --noEmit",
"depcheck": "depcheck",
"eslint": "eslint src/**/*.ts",
"eslint:fix": "eslint src/**/*.ts --fix",
"lint": "npm-run-all eslint check-types"
"lint": "npm-run-all eslint check-types",
"doc": "typedoc --tsconfig tsconfig.base.json --entryPoints src --entryPointStrategy expand --plugin typedoc-plugin-rename-defaults --out ./docs"
},
"keywords": [
"webauthn",
Expand Down Expand Up @@ -49,25 +51,34 @@
"fido-mds3": "^0.3.3-20220606",
"jsrsasign": "^10.5.26",
"lodash.get": "^4.4.2",
"parse-cosekey": "^1.0.1",
"parse-cosekey": "^1.0.2",
"psl": "^1.8.0",
"str2ab": "^1.1.0"
"str2ab": "^1.2.0"
},
"devDependencies": {
"@types/jsrsasign": "^10.5.2",
"@types/jsrsasign": "^10.5.3",
"@types/lodash.get": "^4.4.6",
"@types/node": "^14.17.0",
"@types/psl": "^1.1.0",
"@types/sinon": "^10.0.13",
"@types/tap": "^15.0.7",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"coveralls": "^3.1.1",
"depcheck": "^1.4.3",
"eslint": "^8.17.0",
"eslint": "^8.25.0",
"eslint-config-prettier": "^8.5.0",
"gh-pages": "^4.0.0",
"mockttp": "^3.3.1",
"npm-run-all": "^4.1.5",
"prettier": "^2.6.2",
"ts-node": "^10.0.0",
"prettier": "^2.7.1",
"sinon": "^14.0.0",
"tap": "^16.3.0",
"ts-node": "^10.9.1",
"tsc-alias": "^1.6.6",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.6.3"
"typedoc": "^0.23.13",
"typedoc-plugin-rename-defaults": "^0.6.4",
"typescript": "^4.8.4"
}
}
6 changes: 3 additions & 3 deletions src/assertion/requestOptionsBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ class AssertionRequestOptionsBuilder {

if (this.options.allowCredentials && this.options.allowCredentials.length > 0) {
const isValid = this.options.allowCredentials.every((c: FslPublicKeyCredentialDescriptor) => {
if (!c.type) {
if (c.type == null || c.type !== 'public-key') {
return false;
}
if (!c.id) {
if (c.id == null || c.id.byteLength === 0) {
return false;
}

Expand All @@ -92,7 +92,7 @@ class AssertionRequestOptionsBuilder {
try {
ExtensionValidator.validateAuthenticationExtensions(this.options.extensions);
} catch (error) {
errorMessages.push(`PublicKeyCredentialRequestOptions.allowCredentials is not valid: ${error.message}`);
errorMessages.push(`PublicKeyCredentialRequestOptions.extensions is not valid: ${error.message}`);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/assertion/responseVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class AssertionResponseVerifier {
expect: this.expectation.userId,
});
}
result.userHandle = this.credential.response.userHandle;
}
} else {
if (this.credential.response.userHandle) {
Expand Down Expand Up @@ -83,7 +84,7 @@ class AssertionResponseVerifier {
}
if (
clientDataJSON.tokenBinding.status == null ||
!['present', 'supported', 'not-supported'].includes(clientDataJSON.tokenBinding)
!['present', 'supported', 'not-supported'].includes(clientDataJSON.tokenBinding.status)
) {
throw new FslAssertionVerifyError(
`response.clientDataJSON.tokenBinding.status is invalid: ${clientDataJSON.tokenBinding.status}`
Expand Down Expand Up @@ -129,6 +130,7 @@ class AssertionResponseVerifier {

// step14
if (this.expectation.tokenBinding) {
// TODO
this.verifyTokenBinding(clientData.tokenBinding);
}

Expand Down Expand Up @@ -252,7 +254,7 @@ class AssertionResponseVerifier {
const signatureVerification = verify.verify(credentialPublicKey, str2ab.arraybuffer2buffer(sig));
if (!signatureVerification) {
result.verification = false;
throw new FslAssertionVerifyError('signature is unverifiable,');
throw new FslAssertionVerifyError('signature is unverifiable.');
}

// step21
Expand Down
6 changes: 3 additions & 3 deletions src/attestation/format/androidKey/androidKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import jsrsasign from 'jsrsasign';
import { FslAttestationResult, FslAttestationExpectation } from '../../../type';
import FormatBase from '../formatBase';
import FormatVerifyResult from '../formatVerifyResult';
import KeyUtils from '../../../key/keyUtils';
import EqualUtils from '../../../util/equalUtils';
import CertificateUtils from '../../../certificate/certificateUtils';
import Asn1DecodeUtils from '../../../key/asn1DecodeUtils';
import FslFormatVerifyError from '../../../error/formatVerifyError';
Expand Down Expand Up @@ -60,7 +60,7 @@ class AndroidKeyFormat extends FormatBase {
if (this.result.pem == null) {
throw new FslFormatVerifyError('Credential public key does not exist', AndroidKeyFormat.getName());
}
const matchCredCert = KeyUtils.isEqualPem(credCertPem, this.result.pem);
const matchCredCert = EqualUtils.equalPem(credCertPem, this.result.pem);

// Verify attestationChallenge in extension is identical to clientDataJSONHash
const pX509Cert = new x509.X509Certificate(credCertPem);
Expand All @@ -77,7 +77,7 @@ class AndroidKeyFormat extends FormatBase {
}
const attestationChallengeHex = attestationChallengeExt.content().split('\n')[1]; // TODO ugly
const attestationChallenge = Buffer.from(attestationChallengeHex, 'hex');
const isEqualAttestationChallenge = FormatBase.isEqualBinary(this.result.clientDataJSONHash, attestationChallenge);
const isEqualAttestationChallenge = EqualUtils.equalBinary(this.result.clientDataJSONHash, attestationChallenge);
if (!isEqualAttestationChallenge) {
throw new FslFormatVerifyError(
'AttestationChallenge is not equal.',
Expand Down
5 changes: 0 additions & 5 deletions src/attestation/format/androidSafetynet/androidSafetynet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import jsrsasign from 'jsrsasign';
import str2ab from 'str2ab';
import dayjs from 'dayjs';
import { FslAttestationResult, FslAttestationExpectation } from '../../../type';
import FslUnsupportedError from '../../../error/unsupportedError';
import FslFormatVerifyError from '../../../error/formatVerifyError';
import FormatVerifyResult from '../formatVerifyResult';
import FormatBase from '../formatBase';
Expand All @@ -30,10 +29,6 @@ class AndroidSafetynetFormat extends FormatBase {
}

async verify(): Promise<FormatVerifyResult> {
if (!this.result) {
throw new FslUnsupportedError('set result.');
}

// Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
const decodedAttStmt = this.attStmt;
const ver: string = decodedAttStmt['ver'];
Expand Down
11 changes: 3 additions & 8 deletions src/attestation/format/apple/apple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import jsrsasign from 'jsrsasign';
import * as x509 from '@peculiar/x509';
import { FslAttestationResult, FslAttestationExpectation } from '../../../type';
import FslFormatVerifyError from '../../../error/formatVerifyError';
import FslUnsupportedError from '../../../error/unsupportedError';
import FormatBase from '../formatBase';
import FormatVerifyResult from '../formatVerifyResult';
import KeyUtils from '../../../key/keyUtils';
import EqualUtils from '../../../util/equalUtils';
import CertificateUtils from '../../../certificate/certificateUtils';

class AppleFormat extends FormatBase {
Expand All @@ -29,10 +28,6 @@ class AppleFormat extends FormatBase {
}

async verify(): Promise<FormatVerifyResult> {
if (!this.result) {
throw new FslUnsupportedError('set result.');
}

// Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
const decodedAttStmt = this.attStmt;
const x5c = decodedAttStmt['x5c'] as Buffer[];
Expand All @@ -56,7 +51,7 @@ class AppleFormat extends FormatBase {
return e.type === AppleFormat.NONCE_OID;
});

if (nonceExt == null || !FormatBase.isEqualBinary(nonce, Buffer.from(nonceExt.value).slice(6, 38))) {
if (nonceExt == null || !EqualUtils.equalBinary(nonce, Buffer.from(nonceExt.value).slice(6, 38))) {
throw new FslFormatVerifyError(
'nonce is not equal',
AppleFormat.getName(),
Expand All @@ -71,7 +66,7 @@ class AppleFormat extends FormatBase {
if (this.result.pem == null) {
throw new FslFormatVerifyError('Credential public key does not exist', AppleFormat.getName());
}
const isEqualSPK = KeyUtils.isEqualPem(publicKeyPem, this.result.pem);
const isEqualSPK = EqualUtils.equalPem(publicKeyPem, this.result.pem);
if (!isEqualSPK) {
throw new FslFormatVerifyError(
'credential public key does not equal Subject Public Key of credCert',
Expand Down
5 changes: 2 additions & 3 deletions src/attestation/format/fidoU2f/fidou2f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import crypto from 'crypto';
import jsrsasign from 'jsrsasign';
import { FslAttestationResult, FslAttestationExpectation } from '../../../type';
import FslFormatVerifyError from '../../../error/formatVerifyError';
import FslUnsupportedError from '../../../error/unsupportedError';
import FormatBase from '../formatBase';
import FormatVerifyResult from '../formatVerifyResult';
import CertificateUtils from '../../../certificate/certificateUtils';
Expand All @@ -29,7 +28,7 @@ class FidoU2FFormat extends FormatBase {

async verify(): Promise<FormatVerifyResult> {
if (!this.result) {
throw new FslUnsupportedError('set result.');
throw new FslFormatVerifyError('Data is not enough', FidoU2FFormat.getName());
}

// Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields.
Expand Down Expand Up @@ -71,7 +70,7 @@ class FidoU2FFormat extends FormatBase {
throw new FslFormatVerifyError('Credential public key x is invalid', FidoU2FFormat.getName());
}
const y = coseCredentialPublicKey.get(-3) as Buffer;
if (y == null || x.length !== 32) {
if (y == null || y.length !== 32) {
throw new FslFormatVerifyError('Credential public key y is invalid', FidoU2FFormat.getName());
}

Expand Down
52 changes: 17 additions & 35 deletions src/attestation/format/formatBase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import str2ab from 'str2ab';
import { FslAttestationResult, FslAttestationExpectation } from '../../type';
import FormatVerifyResult from './formatVerifyResult';
import CertificateUtils from '../../certificate/certificateUtils';
Expand All @@ -24,38 +23,6 @@ abstract class FormatBase {
throw new Error('Method not implemented.');
}

static isEqualArrayBuffer(ab1: ArrayBuffer, ab2: ArrayBuffer): boolean {
if (ab1 === ab2) {
return true;
}

if (ab1.byteLength !== ab2.byteLength) {
return false;
}

const dv1 = new DataView(ab1);
const dv2 = new DataView(ab2);

for (let i = 0; i < ab1.byteLength; i++) {
if (dv1.getUint8(i) !== dv2.getUint8(i)) {
return false;
}
}

return true;
}

static isEqualBinary(a: ArrayBuffer | Buffer, b: ArrayBuffer | Buffer): boolean {
if (a === b) {
return true;
}

const aab = a instanceof ArrayBuffer ? a : str2ab.buffer2arraybuffer(a);
const bab = b instanceof ArrayBuffer ? b : str2ab.buffer2arraybuffer(b);

return FormatBase.isEqualArrayBuffer(aab, bab);
}

static async verifyCertificateChain(certificatePEMs: string[], result?: FslAttestationResult): Promise<boolean> {
if (
result != null &&
Expand All @@ -67,9 +34,24 @@ abstract class FormatBase {
const rootCertPEMs: string[] = result.metadataServiceEntry.metadataStatement.attestationRootCertificates.map(
(r: string): string => CertificateUtils.mdsAttestationRootCertificate2pem(r)
);
return rootCertPEMs.some(async (r) => await CertificateUtils.verifyCertificateChain(certificatePEMs, r));
for (const r of rootCertPEMs) {
try {
const isValid = await CertificateUtils.verifyCertificateChain(certificatePEMs, r);
if (isValid) {
return true;
}
} catch (error) {
return false;
}
}

return false;
} else {
return await CertificateUtils.verifyCertificateChain(certificatePEMs);
try {
return await CertificateUtils.verifyCertificateChain(certificatePEMs);
} catch (error) {
return false;
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/attestation/format/none/none.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class NoneFormat extends FormatBase {
this.attStmt = attStmt;
this.result = result;
this.expectation = expectation;
this.configure = config;
}

async verify(): Promise<FormatVerifyResult> {
Expand Down
6 changes: 3 additions & 3 deletions src/attestation/format/packed/packed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as x509 from '@peculiar/x509';
import jsrsasign from 'jsrsasign';
import { FslAttestationResult, FslAttestationExpectation, FslAttestationType } from '../../../type';
import FslFormatVerifyError from '../../../error/formatVerifyError';
import FslUnsupportedError from '../../../error/unsupportedError';
import FormatBase from '../formatBase';
import CertificateUtils from '../../../certificate/certificateUtils';
import FormatVerifyResult from '../formatVerifyResult';
import MdsVerifier from '../../../mds/mdsVerifier';
import EqualUtils from '../../../util/equalUtils';
import str2ab from 'str2ab';

/**
Expand Down Expand Up @@ -35,7 +35,7 @@ class PackedFormat extends FormatBase {

async verify(): Promise<FormatVerifyResult> {
if (!this.result) {
throw new FslUnsupportedError('set result.');
throw new FslFormatVerifyError('Data is not enough', PackedFormat.getName());
}

// step1
Expand Down Expand Up @@ -149,7 +149,7 @@ class PackedFormat extends FormatBase {
}
if (
!this.result.aaguid ||
!FormatBase.isEqualArrayBuffer(str2ab.buffer2arraybuffer(this.result.aaguid.buffer), oidFidoGenCeAaguid.value)
!EqualUtils.equalArrayBuffer(str2ab.buffer2arraybuffer(this.result.aaguid.buffer), oidFidoGenCeAaguid.value)
) {
throw new FslFormatVerifyError('AAGUID is not match.', PackedFormat.getName());
}
Expand Down
Loading

0 comments on commit 9c92ff0

Please sign in to comment.