From 62f8bf89e28a323b96c8a3bb3d107c8023fe0c5d Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 28 May 2017 01:35:21 +0200 Subject: [PATCH] Add typescript type definitions Also add some unit tests that can be run with: `mocha -r ts-node/register test/ts-test.ts` --- .gitignore | 4 +- index.d.ts | 50 +++++++++++++++++++++ package.json | 5 ++- test/ts-test.ts | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 index.d.ts create mode 100644 test/ts-test.ts diff --git a/.gitignore b/.gitignore index 4ce1fde..89b57ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -*.sublime-* \ No newline at end of file +*.sublime-* +.vscode +build diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..4b7977b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,50 @@ +// Type definitions for u2f 0.1.2 +// Project: https://github.com/ashtuchkin/u2f +// Definitions by: Clement Michaud +// Definitions: https://github.com/ashtuchkin/u2f +// TypeScript Version: 2.1 + +export interface Request { + version: "U2F_V2"; + appId: string; + challenge: string; + keyHandle?: string; +} + +export interface RegistrationData { + clientData: string; + registrationData: string; + errorCode?: number; +} + +export interface RegistrationResult { + successful: true; + publicKey: string; + keyHandle: string; + certificate: Buffer; +} + +export interface SignatureData { + clientData: string; + signatureData: string; + errorCode?: number; +} + +export interface SignatureResult { + successful: boolean; + userPresent: boolean; + counter: number; +} + +export interface Error { + errorCode: number; + errorMessage: string; +} + +export function request(appId: string, keyHandle?: string): Request; +export function checkRegistration(request: Request, registerData: RegistrationData): RegistrationResult | Error; +export function checkSignature(request: Request, signData: SignatureData, publicKey: string): SignatureResult | Error; + +// For testing purposes +export function _toWebsafeBase64(buffer: Buffer): string; + diff --git a/package.json b/package.json index 1da73b3..82ee88a 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "u2f", "version": "0.1.2", "description": "U2F 2-factor authentication library", + "types": "index.d.ts", "repository": { "type": "git", "url": "https://github.com/ashtuchkin/u2f" @@ -18,6 +19,8 @@ }, "homepage": "https://github.com/ashtuchkin/u2f", "devDependencies": { - "mocha": "2" + "mocha": "2", + "ts-node": "^4.1.0", + "typescript": "^2.6.2" } } diff --git a/test/ts-test.ts b/test/ts-test.ts new file mode 100644 index 0000000..c38dcdc --- /dev/null +++ b/test/ts-test.ts @@ -0,0 +1,112 @@ + +import * as u2f from "../index"; +import assert = require("assert"); + +interface RegistrationEntry { + keyHandle: string; + publicKey: string; +} + +const USERNAME = "username"; + +describe("FIDO Specification v1.0-rd-20141008 (Typescript)", function () { + describe("checkRegister", function () { + it("should pass test from spec", function () { + const REGISTRATION_APP_ID = "http://example.com"; + + const certificate = new Buffer("3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df", 'hex'); + const clientData = new Buffer('{"typ":"navigator.id.finishEnrollment","challenge":"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}'); + const publicKey = new Buffer("04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9", "hex"); + const keyHandle = new Buffer("2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25", "hex"); + const registrationData = new Buffer("0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871", "hex"); + + // produces RegistrationData + function register_mock(request: u2f.Request): u2f.RegistrationData { + return { + clientData: u2f._toWebsafeBase64(clientData), + registrationData: u2f._toWebsafeBase64(registrationData) + } + } + + function save_mock(username: string, keyHandle: string, publicKey: string): void { + } + + // ********************************************* + // Registration + // ********************************************* + + const registrationRequest = u2f.request(REGISTRATION_APP_ID); + registrationRequest.challenge = "vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo"; + // Client parses the request and produces registration data + const registrationResponse = register_mock(registrationRequest); + const registration = u2f.checkRegistration(registrationRequest, registrationResponse); + + + let registrationResult: u2f.RegistrationResult; + if ((registration).errorCode) + console.log("Error during registration!"); + else { + registrationResult = registration; + save_mock(USERNAME, registrationResult.keyHandle, registrationResult.publicKey); + console.log("Registered!"); + } + + assert(registrationResult); + assert.strictEqual(registrationResult.publicKey, u2f._toWebsafeBase64(publicKey)); + assert.strictEqual(registrationResult.keyHandle, u2f._toWebsafeBase64(keyHandle)); + }); + }); + +// ********************************************* +// Signature +// ********************************************* + + describe("checkSignature", function () { + it("should pass test from spec", function () { + const SIGNATURE_APP_ID = "https://gstatic.com/securitykey/a/example.com"; + + const publicKey = new Buffer("04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d", 'hex'); + const clientData = new Buffer('{"typ":"navigator.id.getAssertion","challenge":"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o","cid_pubkey":{"kty":"EC","crv":"P-256","x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8","y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},"origin":"http://example.com"}'); + const signatureData = new Buffer('0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f', 'hex'); + + + function sign_mock(request: u2f.Request): u2f.SignatureData { + return { + clientData: u2f._toWebsafeBase64(clientData), + signatureData: u2f._toWebsafeBase64(signatureData) + } + } + + + function load_mock(username: string): RegistrationEntry { + return { + keyHandle: "", + publicKey: u2f._toWebsafeBase64(publicKey) + } + } + + + const savedRegistration = load_mock(USERNAME); + + const signatureRequest = u2f.request(SIGNATURE_APP_ID, savedRegistration.keyHandle); + signatureRequest.challenge = "opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o"; + + // Client parses the request and produces signature data + const signatureResponse = sign_mock(signatureRequest); + const signature = u2f.checkSignature(signatureRequest, signatureResponse, savedRegistration.publicKey); + + let signatureResult: u2f.SignatureResult; + if ((signature).errorCode) + console.log("Error during signature!"); + else { + signatureResult = signature; + if (signatureResult.successful) + console.log("Authenticated!"); + } + + assert(signatureResult); + assert.strictEqual(signatureResult.userPresent, true); + assert.strictEqual(signatureResult.counter, 1); + }); + }); +});