├── .nuget ├── NuGet.exe └── NuGet.Config ├── DemoU2FSite ├── Views │ ├── _ViewStart.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Home │ │ ├── CompletedRegister.cshtml │ │ ├── Index.cshtml │ │ ├── Login.cshtml │ │ ├── Register.cshtml │ │ ├── FinishLogin.cshtml │ │ └── FinishRegister.cshtml │ └── Web.config ├── favicon.ico ├── Images │ ├── accent.png │ ├── bullet.png │ ├── heroAccent.png │ ├── orderedList0.png │ ├── orderedList1.png │ ├── orderedList2.png │ ├── orderedList3.png │ ├── orderedList4.png │ ├── orderedList5.png │ ├── orderedList6.png │ ├── orderedList7.png │ ├── orderedList8.png │ └── orderedList9.png ├── Global.asax ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── Content │ └── themes │ │ └── base │ │ ├── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_444444_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_555555_256x240.png │ │ ├── ui-icons_777620_256x240.png │ │ ├── ui-icons_777777_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ ├── ui-icons_cc0000_256x240.png │ │ ├── ui-icons_cd0a0a_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── sortable.css │ │ ├── draggable.css │ │ ├── all.css │ │ ├── autocomplete.css │ │ ├── selectable.css │ │ ├── tooltip.css │ │ ├── base.css │ │ ├── accordion.css │ │ ├── selectmenu.css │ │ ├── menu.css │ │ ├── tabs.css │ │ ├── spinner.css │ │ ├── resizable.css │ │ ├── dialog.css │ │ ├── slider.css │ │ ├── core.css │ │ ├── progressbar.css │ │ ├── button.css │ │ └── datepicker.css ├── App_Start │ ├── FilterConfig.cs │ ├── LegacyAuthorize.cs │ ├── RouteConfig.cs │ ├── WebApiConfig.cs │ └── BundleConfig.cs ├── Scripts │ ├── _references.js │ ├── browser.min.js │ └── jquery.unobtrusive-ajax.min.js ├── Global.asax.cs ├── Bootstrapper.cs ├── Web.Debug.config ├── Web.Release.config ├── Properties │ └── AssemblyInfo.cs ├── packages.config └── Controllers │ └── ProfileController.cs ├── BaseLibrary ├── packages.config ├── IDataContext.cs ├── App.config ├── Properties │ └── AssemblyInfo.cs ├── IUserRepository.cs ├── IMemberShipService.cs └── BaseLibrary.csproj ├── DataModels ├── packages.config ├── ServerRegisterResponse.cs ├── ServerChallenge.cs ├── AuthenticationRequest.cs ├── Device.cs ├── User.cs ├── App.config ├── Properties │ └── AssemblyInfo.cs ├── AccountModels.cs └── DataModels.csproj ├── Repositories ├── packages.config ├── Context │ ├── DataBaseContextInitializer.cs │ └── DataContext.cs ├── Migrations │ ├── 201504031847338_UpdatedDeviceDataModel.cs │ ├── 201507240256246_UpdatedDeviceModelForCounter.cs │ ├── 201501090026319_InitialCreate.Designer.cs │ ├── 201507170044204_MultiDeviesForUsers.Designer.cs │ ├── 201504031847338_UpdatedDeviceDataModel.Designer.cs │ ├── 201507240256246_UpdatedDeviceModelForCounter.Designer.cs │ ├── Configuration.cs │ ├── 201507170044204_MultiDeviesForUsers.cs │ └── 201501090026319_InitialCreate.cs ├── App.config ├── Properties │ └── AssemblyInfo.cs └── UserRepository.cs ├── u2flib.sln.GhostDoc.xml ├── u2flib ├── packages.config ├── Exceptions │ ├── InvalidKeySpecException.cs │ ├── UnsupportedOperationException.cs │ └── U2fException.cs ├── Crypto │ ├── IChallengeGenerator.cs │ ├── RandomChallengeGenerator.cs │ ├── ICrypto.cs │ └── BouncyCastleCrypto.cs ├── Data │ ├── DataObject.cs │ └── Messages │ │ ├── RegisterResponse.cs │ │ ├── StartedRegistration.cs │ │ ├── AuthenticateResponse.cs │ │ ├── ClientData.cs │ │ └── StartedAuthentication.cs ├── Properties │ └── AssemblyInfo.cs └── Util │ └── Utils.cs ├── TraceAndTestImpact.testsettings ├── Local.testsettings ├── u2flib.vsmdi ├── .gitattributes ├── UnitTests ├── packages.config ├── U2F │ ├── RandomChallengeGeneratorUnitTests.cs │ ├── UtilsUnitTests.cs │ ├── Messages │ │ ├── DeviceRegistrationUnitTests.cs │ │ ├── RawRegisterResponseUnitTests.cs │ │ ├── StartedRegistrationUnitTests.cs │ │ ├── StartedAuthenticationUnitTests.cs │ │ ├── AuthenticateResponseUnitTests.cs │ │ ├── RawAuthenticateResponseUnitTests.cs │ │ └── RegisterResponseUnitTests.cs │ └── U2FUnitTests.cs ├── Properties │ └── AssemblyInfo.cs ├── ProfileControllerUnitTests.cs ├── TestConts.cs └── app.config ├── Services ├── Properties │ └── AssemblyInfo.cs └── Services.csproj ├── LICENSE └── .gitignore /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/.nuget/NuGet.exe -------------------------------------------------------------------------------- /DemoU2FSite/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /DemoU2FSite/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/favicon.ico -------------------------------------------------------------------------------- /DemoU2FSite/Images/accent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/accent.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/bullet.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/heroAccent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/heroAccent.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList0.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList1.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList2.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList3.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList4.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList5.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList6.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList7.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList8.png -------------------------------------------------------------------------------- /DemoU2FSite/Images/orderedList9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Images/orderedList9.png -------------------------------------------------------------------------------- /DemoU2FSite/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="DemoU2FSite.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /DemoU2FSite/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /DemoU2FSite/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /DemoU2FSite/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /DemoU2FSite/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /BaseLibrary/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DataModels/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Repositories/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucedog/u2flib/HEAD/DemoU2FSite/Content/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /u2flib.sln.GhostDoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | *.min.js 4 | jquery*.js 5 | 6 | 7 | -------------------------------------------------------------------------------- /DataModels/ServerRegisterResponse.cs: -------------------------------------------------------------------------------- 1 | namespace DataModels 2 | { 3 | public class ServerRegisterResponse 4 | { 5 | public string AppId { get; set; } 6 | public string Challenge { get; set; } 7 | public string Version { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /DataModels/ServerChallenge.cs: -------------------------------------------------------------------------------- 1 | namespace DataModels 2 | { 3 | public class ServerChallenge 4 | { 5 | public string challenge { get; set; } 6 | public string version { get; set; } 7 | public string appId { get; set; } 8 | public string keyHandle { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /DemoU2FSite/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace DemoU2FSite 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /DemoU2FSite/Scripts/_references.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | -------------------------------------------------------------------------------- /DemoU2FSite/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Error"; 5 | } 6 | 7 |
8 |

Error.

9 |

An error occurred while processing your request.

10 |

@Model.Exception.Message

11 |
12 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/sortable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Sortable 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-sortable-handle { 10 | -ms-touch-action: none; 11 | touch-action: none; 12 | } 13 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/draggable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Draggable 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-draggable-handle { 10 | -ms-touch-action: none; 11 | touch-action: none; 12 | } 13 | -------------------------------------------------------------------------------- /u2flib/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/all.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | @import "base.css"; 12 | @import "theme.css"; 13 | -------------------------------------------------------------------------------- /Repositories/Context/DataBaseContextInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | 3 | namespace Repositories.Context 4 | { 5 | public class DataBaseContextInitializer : DropCreateDatabaseIfModelChanges 6 | { 7 | protected override void Seed(DataContext context) 8 | { 9 | // TODO add default user here 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /BaseLibrary/IDataContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using DataModels; 3 | 4 | namespace BaseLibrary 5 | { 6 | public interface IDataContext 7 | { 8 | int SaveChanges(); 9 | 10 | DbSet Users { get; set; } 11 | 12 | DbSet Devices { get; set; } 13 | 14 | DbSet AuthenticationRequests { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/CompletedRegister.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @model DataModels.CompleteRegisterModel 3 | 4 | @{ 5 | ViewBag.Title = "Registeration Completed"; 6 | Layout = "~/Views/Shared/_Layout.cshtml"; 7 | } 8 | 9 |

User: "@Model.UserName" you have successfully registered

10 | 11 |

12 | @Html.ActionLink("Log in", "Login", "Home") now you can log in with Yubico's U2F 13 |

-------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/autocomplete.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Autocomplete 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/autocomplete/#theming 10 | */ 11 | .ui-autocomplete { 12 | position: absolute; 13 | top: 0; 14 | left: 0; 15 | cursor: default; 16 | } 17 | -------------------------------------------------------------------------------- /TraceAndTestImpact.testsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | These are test settings for Trace and Test Impact. 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DemoU2FSite/App_Start/LegacyAuthorize.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace DemoU2FSite 5 | { 6 | public class LegacyAuthorize : AuthorizeAttribute 7 | { 8 | public override void OnAuthorization(AuthorizationContext actionContext) 9 | { 10 | if (!HttpContext.Current.User.Identity.IsAuthenticated) 11 | base.HandleUnauthorizedRequest(actionContext); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/selectable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Selectable 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-selectable { 10 | -ms-touch-action: none; 11 | touch-action: none; 12 | } 13 | .ui-selectable-helper { 14 | position: absolute; 15 | z-index: 100; 16 | border: 1px dotted black; 17 | } 18 | -------------------------------------------------------------------------------- /Local.testsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | These are default test settings for a local test run. 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /u2flib.vsmdi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/tooltip.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Tooltip 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/tooltip/#theming 10 | */ 11 | .ui-tooltip { 12 | padding: 8px; 13 | position: absolute; 14 | z-index: 9999; 15 | max-width: 300px; 16 | -webkit-box-shadow: 0 0 5px #aaa; 17 | box-shadow: 0 0 5px #aaa; 18 | } 19 | body .ui-tooltip { 20 | border-width: 2px; 21 | } 22 | -------------------------------------------------------------------------------- /Repositories/Migrations/201504031847338_UpdatedDeviceDataModel.cs: -------------------------------------------------------------------------------- 1 | namespace Repositories.Migrations 2 | { 3 | using System.Data.Entity.Migrations; 4 | 5 | public partial class UpdatedDeviceDataModel : DbMigration 6 | { 7 | public override void Up() 8 | { 9 | AddColumn("dbo.Devices", "IsCompromised", c => c.Boolean(nullable: false)); 10 | } 11 | 12 | public override void Down() 13 | { 14 | DropColumn("dbo.Devices", "IsCompromised"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Repositories/Migrations/201507240256246_UpdatedDeviceModelForCounter.cs: -------------------------------------------------------------------------------- 1 | namespace Repositories.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class UpdatedDeviceModelForCounter : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | AddColumn("dbo.Devices", "Counter", c => c.Int(nullable: false)); 11 | } 12 | 13 | public override void Down() 14 | { 15 | DropColumn("dbo.Devices", "Counter"); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /DataModels/AuthenticationRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace DataModels 5 | { 6 | public class AuthenticationRequest 7 | { 8 | [Key] 9 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 10 | public int Id { get; set; } 11 | 12 | public string KeyHandle { get; set; } 13 | 14 | public string Challenge { get; set; } 15 | 16 | public string AppId { get; set; } 17 | 18 | public string Version { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /DemoU2FSite/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace DemoU2FSite 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /u2flib/Exceptions/InvalidKeySpecException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Yubico. 3 | * Copyright 2014 Google Inc. All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style 6 | * license that can be found in the LICENSE file or at 7 | * https://developers.google.com/open-source/licenses/bsd 8 | */ 9 | 10 | using System; 11 | 12 | namespace u2flib.Exceptions 13 | { 14 | public class InvalidKeySpecException : Exception 15 | { 16 | public InvalidKeySpecException(string message) 17 | { 18 | Console.WriteLine("Key threw exception: {0}", message); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /u2flib/Crypto/IChallengeGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | namespace u2flib.Crypto 14 | { 15 | public interface IChallengeGenerator 16 | { 17 | /// 18 | /// Generates the challenge. 19 | /// 20 | /// 21 | byte[] GenerateChallenge(); 22 | } 23 | } -------------------------------------------------------------------------------- /UnitTests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DataModels/Device.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace DataModels 6 | { 7 | public class Device 8 | { 9 | [Key] 10 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 11 | public int Id { get; set; } 12 | 13 | public DateTime CreatedOn { get; set; } 14 | 15 | public DateTime UpdatedOn { get; set; } 16 | 17 | public byte[] KeyHandle { get; set; } 18 | 19 | public byte[] PublicKey { get; set; } 20 | 21 | public byte[] AttestationCert { get; set; } 22 | 23 | public int Counter { get; set; } 24 | 25 | public bool IsCompromised { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /DataModels/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace DataModels 7 | { 8 | public class User 9 | { 10 | [Key] 11 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 | public int Id { get; set; } 13 | 14 | public string Name { get; set; } 15 | 16 | public string Password { get; set; } 17 | 18 | public DateTime CreatedOn { get; set; } 19 | 20 | public DateTime UpdatedOn { get; set; } 21 | 22 | public virtual ICollection DeviceRegistrations { get; set; } 23 | 24 | public virtual ICollection AuthenticationRequest { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /u2flib/Exceptions/UnsupportedOperationException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Yubico. 3 | * Copyright 2014 Google Inc. All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style 6 | * license that can be found in the LICENSE file or at 7 | * https://developers.google.com/open-source/licenses/bsd 8 | */ 9 | 10 | using System; 11 | using Org.BouncyCastle.Security; 12 | 13 | namespace u2flib.Exceptions 14 | { 15 | public class UnsupportedOperationException : Exception 16 | { 17 | public UnsupportedOperationException(string errorWhenComputingSha, Exception noSuchAlgorithmException) 18 | { 19 | Console.WriteLine("Error computing sha:{0} No such algorithem exception:{1}", errorWhenComputingSha, noSuchAlgorithmException); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /UnitTests/U2F/RandomChallengeGeneratorUnitTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using u2flib.Crypto; 4 | 5 | namespace UnitTests 6 | { 7 | [TestClass] 8 | public class RandomChallengeGeneratorUnitTests 9 | { 10 | [TestMethod] 11 | public void RandomChallengeGenerator_RandomChanlleges() 12 | { 13 | RandomChallengeGenerator randomChallengeGenerator = new RandomChallengeGenerator(); 14 | byte[] challenge1 = randomChallengeGenerator.GenerateChallenge(); 15 | byte[] challenge2 = randomChallengeGenerator.GenerateChallenge(); 16 | 17 | Assert.AreEqual(challenge1.Length, challenge2.Length); 18 | Assert.IsFalse(challenge1.SequenceEqual(challenge2)); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /BaseLibrary/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /u2flib/Data/DataObject.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using Newtonsoft.Json; 15 | 16 | namespace u2flib.Data 17 | { 18 | public abstract class DataObject 19 | { 20 | public String ToJson() 21 | { 22 | return JsonConvert.SerializeObject(this); 23 | } 24 | 25 | public static T FromJson(String json) 26 | { 27 | return JsonConvert.DeserializeObject(json); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /u2flib/Crypto/RandomChallengeGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Yubico. 3 | * Copyright 2014 Google Inc. All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style 6 | * license that can be found in the LICENSE file or at 7 | * https://developers.google.com/open-source/licenses/bsd 8 | */ 9 | 10 | using Org.BouncyCastle.Security; 11 | 12 | namespace u2flib.Crypto 13 | { 14 | public class RandomChallengeGenerator : IChallengeGenerator 15 | { 16 | // version 1.8.1 of BC uses sha256 be default 17 | private static readonly SecureRandom Random = new SecureRandom(); 18 | 19 | public byte[] GenerateChallenge() 20 | { 21 | byte[] randomBytes = new byte[32]; 22 | Random.NextBytes(randomBytes); 23 | 24 | return randomBytes; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/base.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | @import url("core.css"); 12 | 13 | @import url("accordion.css"); 14 | @import url("autocomplete.css"); 15 | @import url("button.css"); 16 | @import url("datepicker.css"); 17 | @import url("dialog.css"); 18 | @import url("draggable.css"); 19 | @import url("menu.css"); 20 | @import url("progressbar.css"); 21 | @import url("resizable.css"); 22 | @import url("selectable.css"); 23 | @import url("selectmenu.css"); 24 | @import url("sortable.css"); 25 | @import url("slider.css"); 26 | @import url("spinner.css"); 27 | @import url("tabs.css"); 28 | @import url("tooltip.css"); 29 | -------------------------------------------------------------------------------- /DemoU2FSite/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace DemoU2FSite 4 | { 5 | public static class WebApiConfig 6 | { 7 | public static void Register(HttpConfiguration config) 8 | { 9 | config.Routes.MapHttpRoute( 10 | name: "DefaultApi", 11 | routeTemplate: "api/{controller}/{id}", 12 | defaults: new { id = RouteParameter.Optional } 13 | ); 14 | 15 | // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable return type. 16 | // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries. 17 | // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712. 18 | //config.EnableQuerySupport(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /DataModels/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Repositories/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /UnitTests/U2F/UtilsUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Util; 3 | 4 | namespace UnitTests 5 | { 6 | [TestClass] 7 | public class UtilsUnitTests 8 | { 9 | [TestMethod] 10 | public void ByteArrayToBase64String_Test() 11 | { 12 | byte[] testStringByteArray = Utils.GetBytes(TestConts.SERVER_CHALLENGE_REGISTER_BASE64); 13 | 14 | string result = Utils.ByteArrayToBase64String(testStringByteArray); 15 | 16 | Assert.IsFalse(result.Contains("+")); 17 | Assert.IsFalse(result.Contains("/")); 18 | Assert.IsFalse(result.Contains("=")); 19 | } 20 | 21 | [TestMethod] 22 | public void Base64StringToByteArray_Test() 23 | { 24 | byte[] result = Utils.Base64StringToByteArray(TestConts.SERVER_CHALLENGE_REGISTER_BASE64); 25 | 26 | Assert.IsTrue(result.Length > 0); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/accordion.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Accordion 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/accordion/#theming 10 | */ 11 | .ui-accordion .ui-accordion-header { 12 | display: block; 13 | cursor: pointer; 14 | position: relative; 15 | margin: 2px 0 0 0; 16 | padding: .5em .5em .5em .7em; 17 | min-height: 0; /* support: IE7 */ 18 | font-size: 100%; 19 | } 20 | .ui-accordion .ui-accordion-icons { 21 | padding-left: 2.2em; 22 | } 23 | .ui-accordion .ui-accordion-icons .ui-accordion-icons { 24 | padding-left: 2.2em; 25 | } 26 | .ui-accordion .ui-accordion-header .ui-accordion-header-icon { 27 | position: absolute; 28 | left: .5em; 29 | top: 50%; 30 | margin-top: -8px; 31 | } 32 | .ui-accordion .ui-accordion-content { 33 | padding: 1em 2.2em; 34 | border-top: 0; 35 | overflow: auto; 36 | } 37 | -------------------------------------------------------------------------------- /Repositories/Migrations/201501090026319_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Repositories.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.2-31219")] 10 | public sealed partial class InitialCreate : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(InitialCreate)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201501090026319_InitialCreate"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Repositories/Migrations/201507170044204_MultiDeviesForUsers.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Repositories.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.2-31219")] 10 | public sealed partial class MultiDeviesForUsers : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(MultiDeviesForUsers)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201507170044204_MultiDeviesForUsers"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Repositories/Migrations/201504031847338_UpdatedDeviceDataModel.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Repositories.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.2-31219")] 10 | public sealed partial class UpdatedDeviceDataModel : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(UpdatedDeviceDataModel)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201504031847338_UpdatedDeviceDataModel"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Repositories/Migrations/201507240256246_UpdatedDeviceModelForCounter.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Repositories.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.2-31219")] 10 | public sealed partial class UpdatedDeviceModelForCounter : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(UpdatedDeviceModelForCounter)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201507240256246_UpdatedDeviceModelForCounter"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DemoU2FSite/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using System.Web.Http; 3 | using System.Web.Mvc; 4 | using System.Web.Optimization; 5 | using System.Web.Routing; 6 | using Repositories.Context; 7 | 8 | namespace DemoU2FSite 9 | { 10 | // Note: For instructions on enabling IIS6 or IIS7 classic mode, 11 | // visit http://go.microsoft.com/?LinkId=9394801 12 | 13 | public class MvcApplication : System.Web.HttpApplication 14 | { 15 | protected void Application_Start() 16 | { 17 | Database.SetInitializer(new DataBaseContextInitializer()); 18 | 19 | AreaRegistration.RegisterAllAreas(); 20 | //WebApiConfig.Register(GlobalConfiguration.Configuration); 21 | GlobalConfiguration.Configure(WebApiConfig.Register); 22 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 23 | RouteConfig.RegisterRoutes(RouteTable.Routes); 24 | BundleConfig.RegisterBundles(BundleTable.Bundles); 25 | Bootstrapper.Initialise(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | 3 | @{ 4 | ViewBag.Title = "Home Page"; 5 | } 6 | 7 | @section featured 8 | { 9 | 10 |
11 |
12 |

ACP.NET MVC demo site for U2F

13 |

This demo will let you create a user and enroll a U2F device, then authenticate yourself using the enrolled device. This requires a U2F device, as well as a browser with U2F support. Start by registering a user, then try logging in.

14 |

15 | Learn more » 16 |

17 |

18 | @Html.ActionLink("Login", "Login", "Home", new {@class = "btn btn-lg btn-success"}) 19 | @Html.ActionLink("Register", "Register", "Home", new {@class = "btn btn-lg btn-success"}) 20 |

21 |
22 |
23 | } 24 | -------------------------------------------------------------------------------- /u2flib/Exceptions/U2fException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Yubico. 3 | * Copyright 2014 Google Inc. All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style 6 | * license that can be found in the LICENSE file or at 7 | * https://developers.google.com/open-source/licenses/bsd 8 | */ 9 | 10 | using System; 11 | using Org.BouncyCastle.Security; 12 | 13 | namespace u2flib.Exceptions 14 | { 15 | public class U2fException : Exception 16 | { 17 | public U2fException(string message) 18 | { 19 | Console.WriteLine("U2f exception:{0}", message); 20 | } 21 | 22 | public U2fException(string errorWhenVerifyingSignature, InvalidKeyException invalidKeyException) 23 | { 24 | Console.WriteLine("Error verifying signature:{0} invalid key exception:{1}", errorWhenVerifyingSignature, invalidKeyException); 25 | } 26 | 27 | public U2fException(string couldNotParseUserPublicKey, Exception invalidKeyException) 28 | { 29 | Console.WriteLine("Could not parse:{0} invalid key exception:{1}", couldNotParseUserPublicKey, 30 | invalidKeyException); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Repositories/Migrations/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace Repositories.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Migrations; 6 | using System.Linq; 7 | 8 | internal sealed class Configuration : DbMigrationsConfiguration 9 | { 10 | public Configuration() 11 | { 12 | AutomaticMigrationsEnabled = false; 13 | ContextKey = "Repositories.Context.DataContext"; 14 | } 15 | 16 | protected override void Seed(Repositories.Context.DataContext context) 17 | { 18 | // This method will be called after migrating to the latest version. 19 | 20 | // You can use the DbSet.AddOrUpdate() helper extension method 21 | // to avoid creating duplicate seed data. E.g. 22 | // 23 | // context.People.AddOrUpdate( 24 | // p => p.FullName, 25 | // new Person { FullName = "Andrew Peters" }, 26 | // new Person { FullName = "Brice Lambson" }, 27 | // new Person { FullName = "Rowan Miller" } 28 | // ); 29 | // 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /DemoU2FSite/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization; 2 | 3 | namespace DemoU2FSite 4 | { 5 | public class BundleConfig 6 | { 7 | // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725 8 | public static void RegisterBundles(BundleCollection bundles) 9 | { 10 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 | "~/Scripts/jquery-{version}.js")); 12 | 13 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 14 | "~/Scripts/bootstrap.min.js")); 15 | 16 | bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include( 17 | "~/Scripts/jquery-ui-{version}.js")); 18 | 19 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 20 | "~/Scripts/jquery.unobtrusive*", 21 | "~/Scripts/jquery.validate*")); 22 | 23 | bundles.Add(new ScriptBundle("~/bundles/browser").Include( 24 | "~/Scripts/browser.min.js")); 25 | 26 | bundles.Add(new StyleBundle("~/Content/bootstrap").Include("~/Content/bootstrap.min.css")); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /DemoU2FSite/Bootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using BaseLibrary; 3 | using Services; 4 | using Repositories; 5 | using Microsoft.Practices.Unity; 6 | using Unity.Mvc4; 7 | 8 | namespace DemoU2FSite 9 | { 10 | public static class Bootstrapper 11 | { 12 | public static IUnityContainer Initialise() 13 | { 14 | var container = BuildUnityContainer(); 15 | 16 | DependencyResolver.SetResolver(new UnityDependencyResolver(container)); 17 | 18 | return container; 19 | } 20 | 21 | private static IUnityContainer BuildUnityContainer() 22 | { 23 | var container = new UnityContainer(); 24 | 25 | // register all your components with the container here 26 | // it is NOT necessary to register your controllers 27 | 28 | // e.g. container.RegisterType(); 29 | container.RegisterType(); 30 | container.RegisterType(); 31 | container.RegisterType(); 32 | RegisterTypes(container); 33 | 34 | return container; 35 | } 36 | 37 | public static void RegisterTypes(IUnityContainer container) 38 | { 39 | 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /DemoU2FSite/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Repositories/Migrations/201507170044204_MultiDeviesForUsers.cs: -------------------------------------------------------------------------------- 1 | namespace Repositories.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class MultiDeviesForUsers : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | DropIndex("dbo.Users", new[] { "AuthenticationRequest_Id" }); 11 | DropForeignKey("dbo.Users", "FK_dbo.Users_dbo.AuthenticationRequests_AuthenticationRequest_Id"); 12 | AddColumn("dbo.AuthenticationRequests", "User_Id", c => c.Int()); 13 | AddForeignKey("dbo.AuthenticationRequests", "User_Id", "dbo.Users"); 14 | CreateIndex("dbo.AuthenticationRequests", "User_Id"); 15 | DropColumn("dbo.Users", "AuthenticationRequest_Id"); 16 | } 17 | 18 | public override void Down() 19 | { 20 | AddColumn("dbo.Users", "AuthenticationRequest_Id", c => c.Int()); 21 | DropIndex("dbo.AuthenticationRequests", "User_Id"); 22 | DropForeignKey("dbo.AuthenticationRequests", "FK_dbo.Users_dbo.AuthenticationRequests_User_Id"); 23 | DropColumn("dbo.AuthenticationRequests", "User_Id"); 24 | AddForeignKey("dbo.Users", "AuthenticationRequest_Id", "dbo.AuthenticationRequests"); 25 | CreateIndex("dbo.Users", "AuthenticationRequest_Id"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/selectmenu.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Selectmenu 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/selectmenu/#theming 10 | */ 11 | .ui-selectmenu-menu { 12 | padding: 0; 13 | margin: 0; 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | display: none; 18 | } 19 | .ui-selectmenu-menu .ui-menu { 20 | overflow: auto; 21 | /* Support: IE7 */ 22 | overflow-x: hidden; 23 | padding-bottom: 1px; 24 | } 25 | .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { 26 | font-size: 1em; 27 | font-weight: bold; 28 | line-height: 1.5; 29 | padding: 2px 0.4em; 30 | margin: 0.5em 0 0 0; 31 | height: auto; 32 | border: 0; 33 | } 34 | .ui-selectmenu-open { 35 | display: block; 36 | } 37 | .ui-selectmenu-button { 38 | display: inline-block; 39 | overflow: hidden; 40 | position: relative; 41 | text-decoration: none; 42 | cursor: pointer; 43 | } 44 | .ui-selectmenu-button span.ui-icon { 45 | right: 0.5em; 46 | left: auto; 47 | margin-top: -8px; 48 | position: absolute; 49 | top: 50%; 50 | } 51 | .ui-selectmenu-button span.ui-selectmenu-text { 52 | text-align: left; 53 | padding: 0.4em 2.1em 0.4em 1em; 54 | display: block; 55 | line-height: 1.4; 56 | overflow: hidden; 57 | text-overflow: ellipsis; 58 | white-space: nowrap; 59 | } 60 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/menu.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Menu 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/menu/#theming 10 | */ 11 | .ui-menu { 12 | list-style: none; 13 | padding: 0; 14 | margin: 0; 15 | display: block; 16 | outline: none; 17 | } 18 | .ui-menu .ui-menu { 19 | position: absolute; 20 | } 21 | .ui-menu .ui-menu-item { 22 | position: relative; 23 | margin: 0; 24 | padding: 3px 1em 3px .4em; 25 | cursor: pointer; 26 | min-height: 0; /* support: IE7 */ 27 | /* support: IE10, see #8844 */ 28 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); 29 | } 30 | .ui-menu .ui-menu-divider { 31 | margin: 5px 0; 32 | height: 0; 33 | font-size: 0; 34 | line-height: 0; 35 | border-width: 1px 0 0 0; 36 | } 37 | .ui-menu .ui-state-focus, 38 | .ui-menu .ui-state-active { 39 | margin: -1px; 40 | } 41 | 42 | /* icon support */ 43 | .ui-menu-icons { 44 | position: relative; 45 | } 46 | .ui-menu-icons .ui-menu-item { 47 | padding-left: 2em; 48 | } 49 | 50 | /* left-aligned */ 51 | .ui-menu .ui-icon { 52 | position: absolute; 53 | top: 0; 54 | bottom: 0; 55 | left: .2em; 56 | margin: auto 0; 57 | } 58 | 59 | /* right-aligned */ 60 | .ui-menu .ui-menu-icon { 61 | left: auto; 62 | right: 0; 63 | } 64 | -------------------------------------------------------------------------------- /DemoU2FSite/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/tabs.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Tabs 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/tabs/#theming 10 | */ 11 | .ui-tabs { 12 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 13 | padding: .2em; 14 | } 15 | .ui-tabs .ui-tabs-nav { 16 | margin: 0; 17 | padding: .2em .2em 0; 18 | } 19 | .ui-tabs .ui-tabs-nav li { 20 | list-style: none; 21 | float: left; 22 | position: relative; 23 | top: 0; 24 | margin: 1px .2em 0 0; 25 | border-bottom-width: 0; 26 | padding: 0; 27 | white-space: nowrap; 28 | } 29 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor { 30 | float: left; 31 | padding: .5em 1em; 32 | text-decoration: none; 33 | } 34 | .ui-tabs .ui-tabs-nav li.ui-tabs-active { 35 | margin-bottom: -1px; 36 | padding-bottom: 1px; 37 | } 38 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, 39 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, 40 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { 41 | cursor: text; 42 | } 43 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { 44 | cursor: pointer; 45 | } 46 | .ui-tabs .ui-tabs-panel { 47 | display: block; 48 | border-width: 0; 49 | padding: 1em 1.4em; 50 | background: none; 51 | } 52 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/spinner.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Spinner 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/spinner/#theming 10 | */ 11 | .ui-spinner { 12 | position: relative; 13 | display: inline-block; 14 | overflow: hidden; 15 | padding: 0; 16 | vertical-align: middle; 17 | } 18 | .ui-spinner-input { 19 | border: none; 20 | background: none; 21 | color: inherit; 22 | padding: 0; 23 | margin: .2em 0; 24 | vertical-align: middle; 25 | margin-left: .4em; 26 | margin-right: 22px; 27 | } 28 | .ui-spinner-button { 29 | width: 16px; 30 | height: 50%; 31 | font-size: .5em; 32 | padding: 0; 33 | margin: 0; 34 | text-align: center; 35 | position: absolute; 36 | cursor: default; 37 | display: block; 38 | overflow: hidden; 39 | right: 0; 40 | } 41 | /* more specificity required here to override default borders */ 42 | .ui-spinner a.ui-spinner-button { 43 | border-top: none; 44 | border-bottom: none; 45 | border-right: none; 46 | } 47 | /* vertically center icon */ 48 | .ui-spinner .ui-icon { 49 | position: absolute; 50 | margin-top: -8px; 51 | top: 50%; 52 | left: 0; 53 | } 54 | .ui-spinner-up { 55 | top: 0; 56 | } 57 | .ui-spinner-down { 58 | bottom: 0; 59 | } 60 | 61 | /* TR overrides */ 62 | .ui-spinner .ui-icon-triangle-1-s { 63 | /* need to fix icons sprite */ 64 | background-position: -65px -16px; 65 | } 66 | -------------------------------------------------------------------------------- /UnitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UnitTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnitTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("41f8b346-370f-478b-846b-62f057d4bb43")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /DemoU2FSite/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DemoU2FSite")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DemoU2FSite")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6b64c350-6aed-421a-9d58-5d9e8cd08c2c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /u2flib/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("u2flib")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("u2flib")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("849bff81-3d9c-490b-8833-544fd57c2e8a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.6.0")] 36 | [assembly: AssemblyFileVersion("1.0.6.0")] 37 | -------------------------------------------------------------------------------- /Services/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Services")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Services")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("21e0bd12-13e3-4432-89d2-4ba77b7c99bb")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /BaseLibrary/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BaseLibrary")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BaseLibrary")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3c6370f6-4616-429a-b1d3-1bfa57a28a8f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DataModels/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DataModels")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DataModels")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c996de76-1ecd-4317-aee7-d5dde561cf58")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Repositories/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Repositories")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Repositories")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a861b764-39f2-4fb4-9d96-833ea4b7b634")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/resizable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Resizable 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | */ 9 | .ui-resizable { 10 | position: relative; 11 | } 12 | .ui-resizable-handle { 13 | position: absolute; 14 | font-size: 0.1px; 15 | display: block; 16 | -ms-touch-action: none; 17 | touch-action: none; 18 | } 19 | .ui-resizable-disabled .ui-resizable-handle, 20 | .ui-resizable-autohide .ui-resizable-handle { 21 | display: none; 22 | } 23 | .ui-resizable-n { 24 | cursor: n-resize; 25 | height: 7px; 26 | width: 100%; 27 | top: -5px; 28 | left: 0; 29 | } 30 | .ui-resizable-s { 31 | cursor: s-resize; 32 | height: 7px; 33 | width: 100%; 34 | bottom: -5px; 35 | left: 0; 36 | } 37 | .ui-resizable-e { 38 | cursor: e-resize; 39 | width: 7px; 40 | right: -5px; 41 | top: 0; 42 | height: 100%; 43 | } 44 | .ui-resizable-w { 45 | cursor: w-resize; 46 | width: 7px; 47 | left: -5px; 48 | top: 0; 49 | height: 100%; 50 | } 51 | .ui-resizable-se { 52 | cursor: se-resize; 53 | width: 12px; 54 | height: 12px; 55 | right: 1px; 56 | bottom: 1px; 57 | } 58 | .ui-resizable-sw { 59 | cursor: sw-resize; 60 | width: 9px; 61 | height: 9px; 62 | left: -5px; 63 | bottom: -5px; 64 | } 65 | .ui-resizable-nw { 66 | cursor: nw-resize; 67 | width: 9px; 68 | height: 9px; 69 | left: -5px; 70 | top: -5px; 71 | } 72 | .ui-resizable-ne { 73 | cursor: ne-resize; 74 | width: 9px; 75 | height: 9px; 76 | right: -5px; 77 | top: -5px; 78 | } 79 | -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/Login.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @model DataModels.BeginLoginModel 3 | 4 | @{ 5 | ViewBag.Title = "Home Page"; 6 | } 7 | 8 | @section featured { 9 | 10 | @using (Html.BeginForm("BeginLogin", "Home")) 11 | { 12 | @Html.AntiForgeryToken() 13 | @Html.ValidationSummary(true) 14 |
15 | Login 16 |

Enter a username and password.

17 | 18 |
19 |
20 | @Html.LabelFor(m => m.UserName) 21 |
22 |
23 |
24 |
25 | @Html.TextBoxFor(m => m.UserName) 26 | @Html.ValidationMessageFor(m => m.UserName) 27 |
28 |
29 |
30 |
31 | @Html.LabelFor(m => m.Password) 32 |
33 |
34 |
35 |
36 | @Html.PasswordFor(m => m.Password) 37 | @Html.ValidationMessageFor(m => m.Password) 38 |
39 |
40 |
41 | 42 |
43 | } 44 | } 45 | 46 | @section Scripts { 47 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/dialog.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Dialog 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/dialog/#theming 10 | */ 11 | .ui-dialog { 12 | overflow: hidden; 13 | position: absolute; 14 | top: 0; 15 | left: 0; 16 | padding: .2em; 17 | outline: 0; 18 | } 19 | .ui-dialog .ui-dialog-titlebar { 20 | padding: .4em 1em; 21 | position: relative; 22 | } 23 | .ui-dialog .ui-dialog-title { 24 | float: left; 25 | margin: .1em 0; 26 | white-space: nowrap; 27 | width: 90%; 28 | overflow: hidden; 29 | text-overflow: ellipsis; 30 | } 31 | .ui-dialog .ui-dialog-titlebar-close { 32 | position: absolute; 33 | right: .3em; 34 | top: 50%; 35 | width: 20px; 36 | margin: -10px 0 0 0; 37 | padding: 1px; 38 | height: 20px; 39 | } 40 | .ui-dialog .ui-dialog-content { 41 | position: relative; 42 | border: 0; 43 | padding: .5em 1em; 44 | background: none; 45 | overflow: auto; 46 | } 47 | .ui-dialog .ui-dialog-buttonpane { 48 | text-align: left; 49 | border-width: 1px 0 0 0; 50 | background-image: none; 51 | margin-top: .5em; 52 | padding: .3em 1em .5em .4em; 53 | } 54 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { 55 | float: right; 56 | } 57 | .ui-dialog .ui-dialog-buttonpane button { 58 | margin: .5em .4em .5em 0; 59 | cursor: pointer; 60 | } 61 | .ui-dialog .ui-resizable-se { 62 | width: 12px; 63 | height: 12px; 64 | right: -5px; 65 | bottom: -5px; 66 | background-position: 16px 16px; 67 | } 68 | .ui-draggable .ui-dialog-titlebar { 69 | cursor: move; 70 | } 71 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/slider.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Slider 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/slider/#theming 10 | */ 11 | .ui-slider { 12 | position: relative; 13 | text-align: left; 14 | } 15 | .ui-slider .ui-slider-handle { 16 | position: absolute; 17 | z-index: 2; 18 | width: 1.2em; 19 | height: 1.2em; 20 | cursor: default; 21 | -ms-touch-action: none; 22 | touch-action: none; 23 | } 24 | .ui-slider .ui-slider-range { 25 | position: absolute; 26 | z-index: 1; 27 | font-size: .7em; 28 | display: block; 29 | border: 0; 30 | background-position: 0 0; 31 | } 32 | 33 | /* support: IE8 - See #6727 */ 34 | .ui-slider.ui-state-disabled .ui-slider-handle, 35 | .ui-slider.ui-state-disabled .ui-slider-range { 36 | filter: inherit; 37 | } 38 | 39 | .ui-slider-horizontal { 40 | height: .8em; 41 | } 42 | .ui-slider-horizontal .ui-slider-handle { 43 | top: -.3em; 44 | margin-left: -.6em; 45 | } 46 | .ui-slider-horizontal .ui-slider-range { 47 | top: 0; 48 | height: 100%; 49 | } 50 | .ui-slider-horizontal .ui-slider-range-min { 51 | left: 0; 52 | } 53 | .ui-slider-horizontal .ui-slider-range-max { 54 | right: 0; 55 | } 56 | 57 | .ui-slider-vertical { 58 | width: .8em; 59 | height: 100px; 60 | } 61 | .ui-slider-vertical .ui-slider-handle { 62 | left: -.3em; 63 | margin-left: 0; 64 | margin-bottom: -.6em; 65 | } 66 | .ui-slider-vertical .ui-slider-range { 67 | left: 0; 68 | width: 100%; 69 | } 70 | .ui-slider-vertical .ui-slider-range-min { 71 | bottom: 0; 72 | } 73 | .ui-slider-vertical .ui-slider-range-max { 74 | top: 0; 75 | } 76 | -------------------------------------------------------------------------------- /Repositories/Context/DataContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Data.Entity.Validation; 4 | using BaseLibrary; 5 | using DataModels; 6 | 7 | namespace Repositories.Context 8 | { 9 | public class DataContext : DbContext, IDataContext 10 | { 11 | public DataContext() : base("DataContext") { } 12 | 13 | public override int SaveChanges() 14 | { 15 | try 16 | { 17 | return base.SaveChanges(); 18 | } 19 | catch (DbEntityValidationException e) 20 | { 21 | foreach (var eve in e.EntityValidationErrors) 22 | { 23 | Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", 24 | eve.Entry.Entity.GetType().Name, eve.Entry.State); 25 | 26 | foreach (var ve in eve.ValidationErrors) 27 | { 28 | Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage); 29 | } 30 | } 31 | // zero means no items were saved to the DB 32 | return 0; 33 | } 34 | catch (Exception e) 35 | { 36 | Console.WriteLine(e.Message); 37 | // zero means no items were saved to the DB 38 | return 0; 39 | } 40 | } 41 | 42 | public virtual DbSet Users { get; set; } 43 | 44 | public virtual DbSet Devices { get; set; } 45 | 46 | public virtual DbSet AuthenticationRequests { get; set; } 47 | } 48 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/core.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | 12 | /* Layout helpers 13 | ----------------------------------*/ 14 | .ui-helper-hidden { 15 | display: none; 16 | } 17 | .ui-helper-hidden-accessible { 18 | border: 0; 19 | clip: rect(0 0 0 0); 20 | height: 1px; 21 | margin: -1px; 22 | overflow: hidden; 23 | padding: 0; 24 | position: absolute; 25 | width: 1px; 26 | } 27 | .ui-helper-reset { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | outline: 0; 32 | line-height: 1.3; 33 | text-decoration: none; 34 | font-size: 100%; 35 | list-style: none; 36 | } 37 | .ui-helper-clearfix:before, 38 | .ui-helper-clearfix:after { 39 | content: ""; 40 | display: table; 41 | border-collapse: collapse; 42 | } 43 | .ui-helper-clearfix:after { 44 | clear: both; 45 | } 46 | .ui-helper-clearfix { 47 | min-height: 0; /* support: IE7 */ 48 | } 49 | .ui-helper-zfix { 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | position: absolute; 55 | opacity: 0; 56 | filter:Alpha(Opacity=0); /* support: IE8 */ 57 | } 58 | 59 | .ui-front { 60 | z-index: 100; 61 | } 62 | 63 | 64 | /* Interaction Cues 65 | ----------------------------------*/ 66 | .ui-state-disabled { 67 | cursor: default !important; 68 | } 69 | 70 | 71 | /* Icons 72 | ----------------------------------*/ 73 | 74 | /* states and images */ 75 | .ui-icon { 76 | display: block; 77 | text-indent: -99999px; 78 | overflow: hidden; 79 | background-repeat: no-repeat; 80 | } 81 | 82 | 83 | /* Misc visuals 84 | ----------------------------------*/ 85 | 86 | /* Overlays */ 87 | .ui-widget-overlay { 88 | position: fixed; 89 | top: 0; 90 | left: 0; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/DeviceRegistrationUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Data; 3 | 4 | namespace UnitTests.Messages 5 | { 6 | [TestClass] 7 | public class DeviceRegistrationUnitTests 8 | { 9 | private const string JsonData ="{\"KeyHandle\":\"KlUt/bdHftZf2EEz+GGWAQsiFbV9p10xW3uej+LjklpgGVUbq2HRZZFlnLrwC0lQ96v+ZmDi4Ab3aGi3ctcMJQ==\"," + 10 | "\"PublicKey\":\"BLF0vEnHyiVLcNLlwgfO6c8XSCDr136jxlUIwm2lG2V8HMa5UvhiFpeTZILaCm09OCalkJXa9s18A+LmA4XS9tk=\"," + 11 | "\"AttestationCert\":\"MIIBPDCB5KADAgECAgpHkBKAABFVlXNSMAoGCCqGSM49BAMCMBcxFTATBgNVBAMTDEdudWJieSBQaWxvdDAeFw0xMjA4MTQxODI5MzJaFw0xMzA4MTQxODI5MzJaMDExLzAtBgNVBAMTJlBpbG90R251YmJ5LTAuNC4xLTQ3OTAxMjgwMDAxMTU1OTU3MzUyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjWF+ZclQjmS8xWc6yCpnmdo8FEZoLCWMRj//31jf0vo+bDeLU9eVxKTf+0GZ7deGLyOrrwIDtLiRG6BWmZThATAKBggqhkjOPQQDAgNHADBEAiBgzbYGHpwiJi0arB2W2McIKbI2ZTHdomiDLLg2vNMN+gIgYxsUWfCeYzAFVyLI2Jt/SIg7kIm4jWDR2XlZArMEEN8=\"," + 12 | "\"Counter\":0}"; 13 | 14 | [TestMethod] 15 | public void DeviceRegistration_FromJson() 16 | { 17 | DeviceRegistration deviceRegistration = DeviceRegistration.FromJson(JsonData); 18 | 19 | Assert.IsNotNull(deviceRegistration); 20 | Assert.IsNotNull(deviceRegistration.KeyHandle); 21 | Assert.IsNotNull(deviceRegistration.PublicKey); 22 | Assert.IsNotNull(deviceRegistration.GetAttestationCertificate()); 23 | Assert.IsNotNull(deviceRegistration.ToJsonWithOutAttestionCert()); 24 | Assert.IsNotNull(deviceRegistration.ToJson()); 25 | Assert.IsTrue(deviceRegistration.PublicKey.Length > 0); 26 | Assert.IsTrue(deviceRegistration.KeyHandle.Length > 0); 27 | Assert.IsTrue(deviceRegistration.GetHashCode() != 0); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /u2flib/Crypto/ICrypto.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Yubico. 3 | * Copyright 2014 Google Inc. All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style 6 | * license that can be found in the LICENSE file or at 7 | * https://developers.google.com/open-source/licenses/bsd 8 | */ 9 | 10 | using System; 11 | using Org.BouncyCastle.Crypto; 12 | using Org.BouncyCastle.X509; 13 | 14 | namespace u2flib.Crypto 15 | { 16 | public interface ICrypto 17 | { 18 | /// 19 | /// Checks the signature. 20 | /// 21 | /// The key. 22 | /// The source. 23 | /// The signature. 24 | /// 25 | bool CheckSignature(ICipherParameters key, byte[] src, byte[] signature); 26 | 27 | /// 28 | /// Checks the signature. 29 | /// 30 | /// The attestation certificate. 31 | /// The signed bytes. 32 | /// The signature. 33 | /// 34 | bool CheckSignature(X509Certificate attestationCertificate, byte[] signedBytes, byte[] signature); 35 | 36 | /// 37 | /// Decodes the public key. 38 | /// 39 | /// The encoded public key. 40 | /// 41 | ICipherParameters DecodePublicKey(byte[] encodedPublicKey); 42 | 43 | /// 44 | /// Hashes the specified bytes. 45 | /// 46 | /// The bytes. 47 | /// 48 | byte[] Hash(byte[] bytes); 49 | 50 | /// 51 | /// Hashes the specified string. 52 | /// 53 | /// The string. 54 | /// 55 | byte[] Hash(String str); 56 | } 57 | } -------------------------------------------------------------------------------- /u2flib/Util/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace u2flib.Util 5 | { 6 | public static class Utils 7 | { 8 | /// 9 | /// Converts byte array to a properly formatted base64 string 10 | /// 11 | /// The argument. 12 | /// 13 | public static string ByteArrayToBase64String(byte[] input) 14 | { 15 | string result = Convert.ToBase64String(input); 16 | result = result.TrimEnd('='); 17 | result = result.Replace('+', '-'); 18 | result = result.Replace('/', '_'); 19 | 20 | return result; 21 | } 22 | 23 | /// 24 | /// Formats string to proper base64 string and returns it as a byte array. 25 | /// 26 | /// The input. 27 | /// 28 | public static byte[] Base64StringToByteArray(string input) 29 | { 30 | input = input.Replace('-', '+'); 31 | input = input.Replace('_', '/'); 32 | 33 | int mod4 = input.Length % 4; 34 | if (mod4 > 0) 35 | { 36 | input += new string('=', 4 - mod4); 37 | } 38 | 39 | return Convert.FromBase64String(input); 40 | } 41 | 42 | /// 43 | /// Convert string into UTF8 byte[] 44 | /// 45 | /// The string to convert. 46 | /// UTF8 encoded byte[] 47 | public static byte[] GetBytes(string stringToConvert) 48 | { 49 | return Encoding.UTF8.GetBytes(stringToConvert); 50 | } 51 | 52 | /// 53 | /// Converts byte[] to UTF8 encoded string 54 | /// 55 | /// 56 | /// UTF8 encoded string 57 | public static string GetString(byte[] bytes) 58 | { 59 | return Encoding.UTF8.GetString(bytes); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/Register.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @model DataModels.RegisterModel 3 | @{ 4 | ViewBag.Title = "Register"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 | @section featured { 9 | 10 | @using (Html.BeginForm("BeginRegister", "Home", FormMethod.Post, new { id = "registerForm" })) 11 | { 12 | @Html.AntiForgeryToken() 13 | @Html.ValidationSummary(true) 14 |
15 | Register Form 16 |

Enter a username and password.

17 | 18 |
19 |
20 | @Html.LabelFor(m => m.UserName) 21 | 22 |
23 |
24 | @Html.TextBoxFor(m => m.UserName) 25 | @Html.ValidationMessageFor(m => m.UserName) 26 |
27 |
28 |
29 |
30 | @Html.LabelFor(m => m.Password) 31 | 32 |
33 |
34 | @Html.PasswordFor(m => m.Password) 35 | @Html.ValidationMessageFor(m => m.Password) 36 |
37 |
38 |
39 |
40 | @Html.LabelFor(m => m.ConfirmPassword) 41 | 42 |
43 |
44 | @Html.PasswordFor(m => m.ConfirmPassword) 45 | @Html.ValidationMessageFor(m => m.ConfirmPassword) 46 |
47 |
48 | 49 | @Html.HiddenFor(m => m.AppId) 50 | @Html.HiddenFor(m => m.Challenge) 51 | @Html.HiddenFor(m => m.Version) 52 |
53 | 54 |
55 | } 56 | } 57 | 58 | @section Scripts { 59 | } 60 | -------------------------------------------------------------------------------- /BaseLibrary/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using DataModels; 2 | 3 | namespace BaseLibrary 4 | { 5 | public interface IUserRepository 6 | { 7 | /// 8 | /// Finds the user. 9 | /// 10 | /// Name of the user. 11 | /// 12 | User FindUser(string userName); 13 | 14 | /// 15 | /// Updates the device counter. 16 | /// 17 | /// Name of the user. 18 | /// The device public key. 19 | /// The counter. 20 | void UpdateDeviceCounter(string userName, byte[] devicePublicKey, uint counter); 21 | 22 | /// 23 | /// Removes the users authentication request. 24 | /// 25 | /// Name of the user. 26 | void RemoveUsersAuthenticationRequests(string userName); 27 | 28 | /// 29 | /// Saves the user authentication request. 30 | /// 31 | /// The username. 32 | /// The application identifier. 33 | /// The challenge. 34 | /// The key handle. 35 | void SaveUserAuthenticationRequest(string username, string appId, string challenge, string keyHandle); 36 | 37 | /// 38 | /// Adds the user. 39 | /// 40 | /// Name of the user. 41 | /// The hashed password. 42 | void AddUser(string userName, string hashedPassword); 43 | 44 | /// 45 | /// Adds the device registration. 46 | /// 47 | /// Name of the user. 48 | /// The attestation cert. 49 | /// The counter. 50 | /// The key handle. 51 | /// The public key. 52 | void AddDeviceRegistration(string userName, byte[] attestationCert, uint counter, byte[] keyHandle, byte[] publicKey); 53 | } 54 | } -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/RawRegisterResponseUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib; 3 | using u2flib.Data.Messages; 4 | using u2flib.Util; 5 | 6 | namespace UnitTests.Messages 7 | { 8 | [TestClass] 9 | public class RawRegisterResponseUnitTests 10 | { 11 | [TestMethod] 12 | public void RawRegisterResponse_FromBase64() 13 | { 14 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 15 | RawRegisterResponse rawAuthenticateResponse = RawRegisterResponse.FromBase64(registerResponse.RegistrationData); 16 | 17 | Assert.IsNotNull(rawAuthenticateResponse); 18 | Assert.IsNotNull(rawAuthenticateResponse.CreateDevice()); 19 | Assert.IsTrue(rawAuthenticateResponse.GetHashCode() != 0); 20 | } 21 | 22 | [TestMethod] 23 | public void RawRegisterResponse_Equals() 24 | { 25 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 26 | RawRegisterResponse rawAuthenticateResponse1 = RawRegisterResponse.FromBase64(registerResponse.RegistrationData); 27 | RawRegisterResponse rawAuthenticateResponse = RawRegisterResponse.FromBase64(registerResponse.RegistrationData); 28 | 29 | Assert.IsTrue(rawAuthenticateResponse.Equals(rawAuthenticateResponse1)); 30 | } 31 | 32 | [TestMethod] 33 | public void RawRegisterResponse_PackBytesToSign() 34 | { 35 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 36 | RawRegisterResponse rawAuthenticateResponse = RawRegisterResponse.FromBase64(registerResponse.RegistrationData); 37 | 38 | byte[] packedBytes = rawAuthenticateResponse.PackBytesToSign( 39 | U2F.Crypto.Hash("appid"), 40 | U2F.Crypto.Hash(TestConts.CLIENT_DATA_REGISTER), 41 | TestConts.KEY_HANDLE_BASE64_BYTE, 42 | TestConts.USER_PUBLIC_KEY_AUTHENTICATE_HEX); 43 | 44 | Assert.IsNotNull(packedBytes); 45 | Assert.IsTrue(packedBytes.Length > 0); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /BaseLibrary/IMemberShipService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DataModels; 3 | 4 | namespace BaseLibrary 5 | { 6 | public interface IMemberShipService 7 | { 8 | bool SaveNewUser(string userName, string password); 9 | 10 | /// 11 | /// Generates the new server challenge for device registration. 12 | /// 13 | /// The username. 14 | /// 15 | ServerRegisterResponse GenerateServerChallenge(string username); 16 | 17 | /// 18 | /// Completes the registration. 19 | /// 20 | /// Name of the user. 21 | /// The device response. 22 | bool CompleteRegistration(string userName, string deviceResponse); 23 | 24 | /// 25 | /// Authenticates the user. 26 | /// 27 | /// Name of the user. 28 | /// The device response. 29 | /// 30 | bool AuthenticateUser(string userName, string deviceResponse); 31 | 32 | /// 33 | /// Determines whether [is user registered] [the specified user name] is completed registered. 34 | /// NOTE: method checks if use also registered a device to return true 35 | /// 36 | /// Name of the user. 37 | /// 38 | bool IsUserRegistered(string userName); 39 | 40 | /// 41 | /// Generates the server challenge for each device a user has registered. 42 | /// NOTE: challenges will not be issued for compromised devices. 43 | /// 44 | /// Name of the user. 45 | /// List a challenge for each device a user has. 46 | List GenerateServerChallenges(string userName); 47 | 48 | /// 49 | /// Determines whether [is valid user name and password] [the specified user name]. 50 | /// 51 | /// Name of the user. 52 | /// The password. 53 | /// 54 | bool IsValidUserNameAndPassword(string userName, string password); 55 | } 56 | } -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/StartedRegistrationUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Data.Messages; 3 | 4 | namespace UnitTests.Messages 5 | { 6 | [TestClass] 7 | public class StartedRegistrationUnitTests 8 | { 9 | private const string JsonData = "{\"Version\":\"U2F_V2\",\"Challenge\":\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\",\"AppId\":\"http://example.com\"}"; 10 | 11 | [TestMethod] 12 | public void StartedRegistration_ConstructsProperly() 13 | { 14 | StartedRegistration startedRegistration = new StartedRegistration(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, TestConts.APP_ID_ENROLL); 15 | 16 | Assert.IsNotNull(startedRegistration); 17 | Assert.IsNotNull(startedRegistration.Version); 18 | Assert.IsNotNull(startedRegistration.Challenge); 19 | Assert.IsNotNull(startedRegistration.AppId); 20 | Assert.IsNotNull(startedRegistration.ToJson()); 21 | Assert.IsTrue(startedRegistration.GetHashCode() != 0); 22 | Assert.AreEqual(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, startedRegistration.Challenge); 23 | Assert.AreEqual(TestConts.APP_ID_ENROLL, startedRegistration.AppId); 24 | } 25 | 26 | [TestMethod] 27 | public void StartedRegistration_FromJson() 28 | { 29 | StartedRegistration startedRegistration = StartedRegistration.FromJson(JsonData); 30 | 31 | Assert.IsNotNull(startedRegistration); 32 | Assert.IsNotNull(startedRegistration.Version); 33 | Assert.IsNotNull(startedRegistration.Challenge); 34 | Assert.IsNotNull(startedRegistration.AppId); 35 | Assert.IsNotNull(startedRegistration.ToJson()); 36 | Assert.IsTrue(startedRegistration.GetHashCode() != 0); 37 | Assert.AreEqual(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, startedRegistration.Challenge); 38 | Assert.AreEqual(TestConts.APP_ID_ENROLL, startedRegistration.AppId); 39 | } 40 | 41 | [TestMethod] 42 | public void StartedRegistration_Equals() 43 | { 44 | StartedRegistration startedRegistration = StartedRegistration.FromJson(JsonData); 45 | StartedRegistration sameStartedRegistration = new StartedRegistration(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, TestConts.APP_ID_ENROLL); 46 | 47 | Assert.IsTrue(startedRegistration.Equals(sameStartedRegistration)); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /DemoU2FSite/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/StartedAuthenticationUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Data.Messages; 3 | 4 | namespace UnitTests.Messages 5 | { 6 | [TestClass] 7 | public class StartedAuthenticationUnitTests 8 | { 9 | private const string JsonData = "{\"Version\":\"U2F_V2\",\"KeyHandle\":\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\",\"Challenge\":\"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o\",\"AppId\":\"http://example.com\"}"; 10 | 11 | [TestMethod] 12 | public void StartedAuthentication_ConstructsProperly() 13 | { 14 | StartedAuthentication startedAuthentication = new StartedAuthentication( 15 | TestConts.SERVER_CHALLENGE_SIGN_BASE64, 16 | TestConts.APP_ID_ENROLL, 17 | TestConts.KEY_HANDLE_BASE64); 18 | 19 | Assert.IsNotNull(startedAuthentication); 20 | Assert.IsNotNull(startedAuthentication.Version); 21 | Assert.IsNotNull(startedAuthentication.ToJson()); 22 | Assert.IsTrue(startedAuthentication.GetHashCode() != 0); 23 | Assert.AreEqual(TestConts.APP_ID_ENROLL, startedAuthentication.AppId); 24 | Assert.AreEqual(TestConts.KEY_HANDLE_BASE64, startedAuthentication.KeyHandle); 25 | Assert.AreEqual(TestConts.SERVER_CHALLENGE_SIGN_BASE64, startedAuthentication.Challenge); 26 | } 27 | 28 | [TestMethod] 29 | public void StartedAuthentication_FromJson() 30 | { 31 | StartedAuthentication startedAuthentication = StartedAuthentication.FromJson(JsonData); 32 | 33 | Assert.IsNotNull(startedAuthentication); 34 | Assert.IsNotNull(startedAuthentication.Version); 35 | Assert.IsNotNull(startedAuthentication.ToJson()); 36 | Assert.IsTrue(startedAuthentication.GetHashCode() != 0); 37 | Assert.AreEqual(TestConts.APP_ID_ENROLL, startedAuthentication.AppId); 38 | Assert.AreEqual(TestConts.KEY_HANDLE_BASE64, startedAuthentication.KeyHandle); 39 | Assert.AreEqual(TestConts.SERVER_CHALLENGE_SIGN_BASE64, startedAuthentication.Challenge); 40 | } 41 | 42 | [TestMethod] 43 | public void StartedAuthentication_Equals() 44 | { 45 | StartedAuthentication sameStartedAuthentication = new StartedAuthentication( 46 | TestConts.SERVER_CHALLENGE_SIGN_BASE64, 47 | TestConts.APP_ID_ENROLL, 48 | TestConts.KEY_HANDLE_BASE64); 49 | StartedAuthentication startedAuthentication = StartedAuthentication.FromJson(JsonData); 50 | 51 | Assert.IsTrue(startedAuthentication.Equals(sameStartedAuthentication)); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Repositories/Migrations/201501090026319_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | namespace Repositories.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class InitialCreate : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.AuthenticationRequests", 12 | c => new 13 | { 14 | Id = c.Int(nullable: false, identity: true), 15 | KeyHandle = c.String(), 16 | Challenge = c.String(), 17 | AppId = c.String(), 18 | Version = c.String(), 19 | }) 20 | .PrimaryKey(t => t.Id); 21 | 22 | CreateTable( 23 | "dbo.Devices", 24 | c => new 25 | { 26 | Id = c.Int(nullable: false, identity: true), 27 | CreatedOn = c.DateTime(nullable: false), 28 | UpdatedOn = c.DateTime(nullable: false), 29 | KeyHandle = c.Binary(), 30 | PublicKey = c.Binary(), 31 | AttestationCert = c.Binary(), 32 | User_Id = c.Int(), 33 | }) 34 | .PrimaryKey(t => t.Id) 35 | .ForeignKey("dbo.Users", t => t.User_Id) 36 | .Index(t => t.User_Id); 37 | 38 | CreateTable( 39 | "dbo.Users", 40 | c => new 41 | { 42 | Id = c.Int(nullable: false, identity: true), 43 | Name = c.String(), 44 | Password = c.String(), 45 | CreatedOn = c.DateTime(nullable: false), 46 | UpdatedOn = c.DateTime(nullable: false), 47 | AuthenticationRequest_Id = c.Int(), 48 | }) 49 | .PrimaryKey(t => t.Id) 50 | .ForeignKey("dbo.AuthenticationRequests", t => t.AuthenticationRequest_Id) 51 | .Index(t => t.AuthenticationRequest_Id); 52 | 53 | } 54 | 55 | public override void Down() 56 | { 57 | DropForeignKey("dbo.DeviceRegistrations", "User_Id", "dbo.Users"); 58 | DropForeignKey("dbo.Users", "AuthenticationRequest_Id", "dbo.AuthenticationRequests"); 59 | DropIndex("dbo.Users", new[] { "AuthenticationRequest_Id" }); 60 | DropIndex("dbo.DeviceRegistrations", new[] { "User_Id" }); 61 | DropTable("dbo.Users"); 62 | DropTable("dbo.DeviceRegistrations"); 63 | DropTable("dbo.AuthenticationRequests"); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DemoU2FSite/Views/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /DataModels/AccountModels.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace DataModels 6 | { 7 | public class UserViewModel 8 | { 9 | [Required] 10 | [Display(Name = "User name")] 11 | public string UserName { get; set; } 12 | 13 | [Required] 14 | public int Id { get; set; } 15 | 16 | [Required] 17 | [Display(Name = "Updated on")] 18 | public DateTime UpdatedOn { get; set; } 19 | 20 | [Required] 21 | [Display(Name = "Created on")] 22 | public DateTime CreatedOn { get; set; } 23 | 24 | [Required] 25 | public ICollection Devices { get; set; } 26 | } 27 | 28 | public class BeginLoginModel 29 | { 30 | [Required] 31 | [Display(Name = "User name")] 32 | public string UserName { get; set; } 33 | 34 | [Required] 35 | [Display(Name = "Password")] 36 | public string Password { get; set; } 37 | } 38 | 39 | public class CompleteLoginModel 40 | { 41 | [Required] 42 | [Display(Name = "User name")] 43 | public string UserName { get; set; } 44 | 45 | [Required] 46 | [Display(Name = "App id")] 47 | public string AppId { get; set; } 48 | 49 | [Required] 50 | [Display(Name = "Version")] 51 | public string Version { get; set; } 52 | 53 | [Required] 54 | [Display(Name = "Device Response")] 55 | public string DeviceResponse { get; set; } 56 | 57 | [Display(Name = "Challenges")] 58 | public string Challenges { get; set; } 59 | } 60 | 61 | public class RegisterModel 62 | { 63 | [Required] 64 | [Display(Name = "User name")] 65 | public string UserName { get; set; } 66 | 67 | [Required] 68 | [Display(Name = "Password")] 69 | public string Password { get; set; } 70 | 71 | [Required] 72 | [Display(Name = "Confirm Password")] 73 | public string ConfirmPassword { get; set; } 74 | 75 | [Display(Name = "Challenge")] 76 | public string Challenge { get; set; } 77 | 78 | [Display(Name = "Version")] 79 | public string Version { get; set; } 80 | 81 | [Display(Name = "App ID")] 82 | public string AppId { get; set; } 83 | } 84 | 85 | public class CompleteRegisterModel 86 | { 87 | [Required] 88 | [Display(Name = "User name")] 89 | public string UserName { get; set; } 90 | 91 | [Display(Name = "Challenge")] 92 | public string Challenge { get; set; } 93 | 94 | [Display(Name = "Version")] 95 | public string Version { get; set; } 96 | 97 | [Display(Name = "App ID")] 98 | public string AppId { get; set; } 99 | 100 | public string DeviceResponse { get; set; } 101 | } 102 | } -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/AuthenticateResponseUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Data.Messages; 3 | 4 | namespace UnitTests.Messages 5 | { 6 | [TestClass] 7 | public class AuthenticateResponseUnitTests 8 | { 9 | private const string JsonData = "{\"SignatureData\":\"AQAAAAEwRAIgS18M0XU0zt2MNO4JVw71QqNT30Q2AwzkPUBt6HC4R3gCICZ7uZj6ybcmbrYOfLC16r39W6lhT1PHsiJy7BAEepI_\",\"ClientData\":" + "\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0\"," + "\"KeyHandle\":\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\"}"; 10 | 11 | [TestMethod] 12 | public void AuthenticateResponse_ConstructsProperly() 13 | { 14 | AuthenticateResponse authenticateResponse = new AuthenticateResponse(TestConts.CLIENT_DATA_AUTHENTICATE_BASE64, 15 | TestConts.SIGN_RESPONSE_DATA_BASE64, 16 | TestConts.KEY_HANDLE_BASE64); 17 | 18 | Assert.IsNotNull(authenticateResponse); 19 | Assert.IsNotNull(authenticateResponse.ToJson()); 20 | Assert.IsNotNull(authenticateResponse.GetClientData()); 21 | Assert.IsTrue(authenticateResponse.GetHashCode() != 0); 22 | Assert.AreEqual(JsonData, authenticateResponse.ToJson()); 23 | Assert.AreEqual(TestConts.SIGN_RESPONSE_DATA_BASE64, authenticateResponse.SignatureData); 24 | Assert.AreEqual(TestConts.KEY_HANDLE_BASE64, authenticateResponse.KeyHandle); 25 | } 26 | 27 | [TestMethod] 28 | public void AuthenticateResponse_FromJson() 29 | { 30 | AuthenticateResponse authenticateResponse = AuthenticateResponse.FromJson(JsonData); 31 | 32 | Assert.IsNotNull(authenticateResponse); 33 | Assert.AreEqual(authenticateResponse.GetType(), typeof(AuthenticateResponse)); 34 | } 35 | 36 | [TestMethod] 37 | public void AuthenticateResponse_Equals() 38 | { 39 | AuthenticateResponse authenticateResponse = AuthenticateResponse.FromJson(JsonData); 40 | AuthenticateResponse sameAuthenticateResponse = new AuthenticateResponse( 41 | TestConts.CLIENT_DATA_AUTHENTICATE_BASE64, 42 | TestConts.SIGN_RESPONSE_DATA_BASE64, 43 | TestConts.KEY_HANDLE_BASE64); 44 | 45 | Assert.IsNotNull(authenticateResponse); 46 | Assert.IsNotNull(sameAuthenticateResponse); 47 | Assert.IsTrue(authenticateResponse.Equals(sameAuthenticateResponse)); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/progressbar.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Progressbar 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/progressbar/#theming 10 | */ 11 | .ui-progressbar { 12 | height: 2em; 13 | text-align: left; 14 | overflow: hidden; 15 | } 16 | .ui-progressbar .ui-progressbar-value { 17 | margin: -1px; 18 | height: 100%; 19 | } 20 | .ui-progressbar .ui-progressbar-overlay { 21 | background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); 22 | height: 100%; 23 | filter: alpha(opacity=25); /* support: IE8 */ 24 | opacity: 0.25; 25 | } 26 | .ui-progressbar-indeterminate .ui-progressbar-value { 27 | background-image: none; 28 | } 29 | -------------------------------------------------------------------------------- /u2flib/Data/Messages/RegisterResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Linq; 15 | 16 | namespace u2flib.Data.Messages 17 | { 18 | public class RegisterResponse : DataObject 19 | { 20 | private readonly ClientData _clientDataRef; 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The registration data. 25 | /// The client data. 26 | public RegisterResponse(String registrationData, String clientData) 27 | { 28 | RegistrationData = registrationData; 29 | ClientData = clientData; 30 | _clientDataRef = new ClientData(ClientData); 31 | } 32 | 33 | /// 34 | /// Gets the registration data. 35 | /// 36 | /// 37 | /// The registration data. 38 | /// 39 | public String RegistrationData { get; private set; } 40 | 41 | /// 42 | /// Gets the Client data. 43 | /// 44 | /// 45 | /// The Client data. 46 | /// 47 | public String ClientData { get; private set; } 48 | 49 | public ClientData GetClientData() 50 | { 51 | return _clientDataRef; 52 | } 53 | 54 | public String GetRequestId() 55 | { 56 | return GetClientData().Challenge; 57 | } 58 | 59 | public override int GetHashCode() 60 | { 61 | int hash = RegistrationData.Sum(c => c + 31); 62 | hash += ClientData.Sum(c => c + 31); 63 | 64 | return hash; 65 | } 66 | 67 | public override bool Equals(Object obj) 68 | { 69 | if (this == obj) 70 | return true; 71 | if (obj == null) 72 | return false; 73 | if (GetType() != obj.GetType()) 74 | return false; 75 | RegisterResponse other = (RegisterResponse) obj; 76 | if (ClientData == null) 77 | { 78 | if (other.ClientData != null) 79 | return false; 80 | } 81 | else if (!ClientData.Equals(other.ClientData)) 82 | return false; 83 | if (RegistrationData == null) 84 | { 85 | if (other.RegistrationData != null) 86 | return false; 87 | } 88 | else if (!RegistrationData.Equals(other.RegistrationData)) 89 | return false; 90 | return true; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/button.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Button 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/button/#theming 10 | */ 11 | .ui-button { 12 | display: inline-block; 13 | position: relative; 14 | padding: 0; 15 | line-height: normal; 16 | margin-right: .1em; 17 | cursor: pointer; 18 | vertical-align: middle; 19 | text-align: center; 20 | overflow: visible; /* removes extra width in IE */ 21 | } 22 | .ui-button, 23 | .ui-button:link, 24 | .ui-button:visited, 25 | .ui-button:hover, 26 | .ui-button:active { 27 | text-decoration: none; 28 | } 29 | /* to make room for the icon, a width needs to be set here */ 30 | .ui-button-icon-only { 31 | width: 2.2em; 32 | } 33 | /* button elements seem to need a little more width */ 34 | button.ui-button-icon-only { 35 | width: 2.4em; 36 | } 37 | .ui-button-icons-only { 38 | width: 3.4em; 39 | } 40 | button.ui-button-icons-only { 41 | width: 3.7em; 42 | } 43 | 44 | /* button text element */ 45 | .ui-button .ui-button-text { 46 | display: block; 47 | line-height: normal; 48 | } 49 | .ui-button-text-only .ui-button-text { 50 | padding: .4em 1em; 51 | } 52 | .ui-button-icon-only .ui-button-text, 53 | .ui-button-icons-only .ui-button-text { 54 | padding: .4em; 55 | text-indent: -9999999px; 56 | } 57 | .ui-button-text-icon-primary .ui-button-text, 58 | .ui-button-text-icons .ui-button-text { 59 | padding: .4em 1em .4em 2.1em; 60 | } 61 | .ui-button-text-icon-secondary .ui-button-text, 62 | .ui-button-text-icons .ui-button-text { 63 | padding: .4em 2.1em .4em 1em; 64 | } 65 | .ui-button-text-icons .ui-button-text { 66 | padding-left: 2.1em; 67 | padding-right: 2.1em; 68 | } 69 | /* no icon support for input elements, provide padding by default */ 70 | input.ui-button { 71 | padding: .4em 1em; 72 | } 73 | 74 | /* button icon element(s) */ 75 | .ui-button-icon-only .ui-icon, 76 | .ui-button-text-icon-primary .ui-icon, 77 | .ui-button-text-icon-secondary .ui-icon, 78 | .ui-button-text-icons .ui-icon, 79 | .ui-button-icons-only .ui-icon { 80 | position: absolute; 81 | top: 50%; 82 | margin-top: -8px; 83 | } 84 | .ui-button-icon-only .ui-icon { 85 | left: 50%; 86 | margin-left: -8px; 87 | } 88 | .ui-button-text-icon-primary .ui-button-icon-primary, 89 | .ui-button-text-icons .ui-button-icon-primary, 90 | .ui-button-icons-only .ui-button-icon-primary { 91 | left: .5em; 92 | } 93 | .ui-button-text-icon-secondary .ui-button-icon-secondary, 94 | .ui-button-text-icons .ui-button-icon-secondary, 95 | .ui-button-icons-only .ui-button-icon-secondary { 96 | right: .5em; 97 | } 98 | 99 | /* button sets */ 100 | .ui-buttonset { 101 | margin-right: 7px; 102 | } 103 | .ui-buttonset .ui-button { 104 | margin-left: 0; 105 | margin-right: -.3em; 106 | } 107 | 108 | /* workarounds */ 109 | /* reset extra padding in Firefox, see h5bp.com/l */ 110 | input.ui-button::-moz-focus-inner, 111 | button.ui-button::-moz-focus-inner { 112 | border: 0; 113 | padding: 0; 114 | } 115 | -------------------------------------------------------------------------------- /UnitTests/ProfileControllerUnitTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Web.Mvc; 4 | using BaseLibrary; 5 | using DataModels; 6 | using DemoU2FSite.Controllers; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using Moq; 9 | using u2flib.Data; 10 | using u2flib.Data.Messages; 11 | using u2flib.Util; 12 | 13 | namespace UnitTests 14 | { 15 | [TestClass] 16 | public class ProfileControllerUnitTests 17 | { 18 | private Mock _userRepository; 19 | private Mock _memberShipService; 20 | private DeviceRegistration _deviceRegistration; 21 | private AuthenticateResponse _authenticateResponse; 22 | private User _user; 23 | 24 | [TestInitialize] 25 | public void Setup() 26 | { 27 | CreateResponses(); 28 | _userRepository = new Mock(); 29 | _memberShipService = new Mock(); 30 | _user = new User 31 | { 32 | Name = "test", 33 | Password = "KSpjLUfp4gaP1Zu4F+6qhcBNhQeJJLRnN1zt9MBHWh8=", 34 | DeviceRegistrations = new Collection 35 | { 36 | new Device 37 | { 38 | KeyHandle = _deviceRegistration.KeyHandle, 39 | PublicKey = _deviceRegistration.PublicKey, 40 | AttestationCert = _deviceRegistration.AttestationCert, 41 | Counter = (int) _deviceRegistration.Counter 42 | } 43 | }, 44 | AuthenticationRequest = new List 45 | { 46 | new AuthenticationRequest 47 | { 48 | AppId = "test", 49 | KeyHandle = _authenticateResponse.KeyHandle, 50 | Challenge = _authenticateResponse.GetClientData().Challenge 51 | } 52 | } 53 | }; 54 | } 55 | 56 | [TestMethod] 57 | public void ProfileController_ConstructsProperly() 58 | { 59 | ProfileController profileController = new ProfileController(_userRepository.Object, _memberShipService.Object); 60 | 61 | Assert.IsNotNull(profileController); 62 | } 63 | 64 | private void CreateResponses() 65 | { 66 | _deviceRegistration = new DeviceRegistration( 67 | TestConts.KEY_HANDLE_BASE64_BYTE, 68 | TestConts.USER_PUBLIC_KEY_AUTHENTICATE_HEX, 69 | Utils.Base64StringToByteArray(TestConts.ATTESTATION_CERTIFICATE), 70 | 0); 71 | 72 | _authenticateResponse = new AuthenticateResponse( 73 | TestConts.CLIENT_DATA_AUTHENTICATE_BASE64, 74 | TestConts.SIGN_RESPONSE_DATA_BASE64, 75 | TestConts.KEY_HANDLE_BASE64); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/RawAuthenticateResponseUnitTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using u2flib; 4 | using u2flib.Data.Messages; 5 | 6 | namespace UnitTests.Messages 7 | { 8 | [TestClass] 9 | public class RawAuthenticateResponseUnitTests 10 | { 11 | private AuthenticateResponse _authenticateResponse= null; 12 | private ClientData clientData; 13 | public const String AuthenticateTyp = "navigator.id.getAssertion"; 14 | public const String RegisterType = "navigator.id.finishEnrollment"; 15 | 16 | [TestInitialize] 17 | public void Setup() 18 | { 19 | StartedAuthentication startedAuthentication = 20 | new StartedAuthentication(TestConts.SERVER_CHALLENGE_SIGN_BASE64, TestConts.APP_ID_ENROLL, 21 | TestConts.KEY_HANDLE_BASE64); 22 | _authenticateResponse = new AuthenticateResponse(TestConts.CLIENT_DATA_AUTHENTICATE_BASE64, 23 | TestConts.SIGN_RESPONSE_DATA_BASE64, 24 | TestConts.KEY_HANDLE_BASE64); 25 | 26 | clientData = _authenticateResponse.GetClientData(); 27 | clientData.CheckContent(AuthenticateTyp, startedAuthentication.Challenge, null); 28 | } 29 | 30 | [TestMethod] 31 | public void RawAuthenticateResponse_FromBase64() 32 | { 33 | RawAuthenticateResponse rawAuthenticateResponse = RawAuthenticateResponse.FromBase64(_authenticateResponse.SignatureData); 34 | 35 | Assert.IsNotNull(rawAuthenticateResponse); 36 | Assert.IsNotNull(rawAuthenticateResponse.UserPresence); 37 | Assert.IsNotNull(rawAuthenticateResponse.ToString()); 38 | Assert.IsTrue(rawAuthenticateResponse.UserPresence > 0); 39 | Assert.IsTrue(rawAuthenticateResponse.GetHashCode() > 0); 40 | Assert.IsTrue(rawAuthenticateResponse.Signature.Length > 0); 41 | } 42 | 43 | [TestMethod] 44 | public void RawAuthenticateResponse_PackBytesToSign() 45 | { 46 | RawAuthenticateResponse rawAuthenticateResponse = RawAuthenticateResponse.FromBase64(_authenticateResponse.SignatureData); 47 | 48 | byte[] signedBytes = rawAuthenticateResponse.PackBytesToSign( 49 | U2F.Crypto.Hash("testid"), 50 | rawAuthenticateResponse.UserPresence, 51 | rawAuthenticateResponse.Counter, 52 | U2F.Crypto.Hash(clientData.AsJson()) 53 | ); 54 | 55 | Assert.IsNotNull(signedBytes); 56 | Assert.IsTrue(signedBytes.Length > 0); 57 | } 58 | 59 | [TestMethod] 60 | public void RawAuthenticateResponse_Equals() 61 | { 62 | RawAuthenticateResponse rawAuthenticateResponse1 = RawAuthenticateResponse.FromBase64(_authenticateResponse.SignatureData); 63 | RawAuthenticateResponse rawAuthenticateResponse = RawAuthenticateResponse.FromBase64(_authenticateResponse.SignatureData); 64 | 65 | Assert.IsTrue(rawAuthenticateResponse1.Equals(rawAuthenticateResponse)); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /DemoU2FSite/Scripts/browser.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bowser - a browser detector 3 | * https://github.com/ded/bowser 4 | * MIT License | (c) Dustin Diaz 2014 5 | */ 6 | !function(e,t){typeof module!="undefined"&&module.exports?module.exports.browser=t():typeof define=="function"&&define.amd?define(t):this[e]=t()}("bowser",function(){function t(t){function n(e){var n=t.match(e);return n&&n.length>1&&n[1]||""}var r=n(/(ipod|iphone|ipad)/i).toLowerCase(),i=/like android/i.test(t),s=!i&&/android/i.test(t),o=n(/version\/(\d+(\.\d+)?)/i),u=/tablet/i.test(t),a=!u&&/[^-]mobi/i.test(t),f;/opera|opr/i.test(t)?f={name:"Opera",opera:e,version:o||n(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)}:/windows phone/i.test(t)?f={name:"Windows Phone",windowsphone:e,msie:e,version:n(/iemobile\/(\d+(\.\d+)?)/i)}:/msie|trident/i.test(t)?f={name:"Internet Explorer",msie:e,version:n(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:/chrome|crios|crmo/i.test(t)?f={name:"Chrome",chrome:e,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:r?(f={name:r=="iphone"?"iPhone":r=="ipad"?"iPad":"iPod"},o&&(f.version=o)):/sailfish/i.test(t)?f={name:"Sailfish",sailfish:e,version:n(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(t)?f={name:"SeaMonkey",seamonkey:e,version:n(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel/i.test(t)?(f={name:"Firefox",firefox:e,version:n(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(t)&&(f.firefoxos=e)):/silk/i.test(t)?f={name:"Amazon Silk",silk:e,version:n(/silk\/(\d+(\.\d+)?)/i)}:s?f={name:"Android",version:o}:/phantom/i.test(t)?f={name:"PhantomJS",phantom:e,version:n(/phantomjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(t)||/rim\stablet/i.test(t)?f={name:"BlackBerry",blackberry:e,version:o||n(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:/(web|hpw)os/i.test(t)?(f={name:"WebOS",webos:e,version:o||n(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(t)&&(f.touchpad=e)):/bada/i.test(t)?f={name:"Bada",bada:e,version:n(/dolfin\/(\d+(\.\d+)?)/i)}:/tizen/i.test(t)?f={name:"Tizen",tizen:e,version:n(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||o}:/safari/i.test(t)?f={name:"Safari",safari:e,version:o}:f={},/(apple)?webkit/i.test(t)?(f.name=f.name||"Webkit",f.webkit=e,!f.version&&o&&(f.version=o)):!f.opera&&/gecko\//i.test(t)&&(f.name=f.name||"Gecko",f.gecko=e,f.version=f.version||n(/gecko\/(\d+(\.\d+)?)/i)),s||f.silk?f.android=e:r&&(f[r]=e,f.ios=e);var l="";r?(l=n(/os (\d+([_\s]\d+)*) like mac os x/i),l=l.replace(/[_\s]/g,".")):s?l=n(/android[ \/-](\d+(\.\d+)*)/i):f.windowsphone?l=n(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):f.webos?l=n(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):f.blackberry?l=n(/rim\stablet\sos\s(\d+(\.\d+)*)/i):f.bada?l=n(/bada\/(\d+(\.\d+)*)/i):f.tizen&&(l=n(/tizen[\/\s](\d+(\.\d+)*)/i)),l&&(f.osversion=l);var c=l.split(".")[0];if(u||r=="ipad"||s&&(c==3||c==4&&!a)||f.silk)f.tablet=e;else if(a||r=="iphone"||r=="ipod"||s||f.blackberry||f.webos||f.bada)f.mobile=e;return f.msie&&f.version>=10||f.chrome&&f.version>=20||f.firefox&&f.version>=20||f.safari&&f.version>=6||f.opera&&f.version>=10||f.ios&&f.osversion&&f.osversion.split(".")[0]>=6||f.blackberry&&f.version>=10.1?f.a=e:f.msie&&f.version<10||f.chrome&&f.version<20||f.firefox&&f.version<20||f.safari&&f.version<6||f.opera&&f.version<10||f.ios&&f.osversion&&f.osversion.split(".")[0]<6?f.c=e:f.x=e,f}var e=!0,n=t(typeof navigator!="undefined"?navigator.userAgent:"");return n._detect=t,n}) -------------------------------------------------------------------------------- /DemoU2FSite/Controllers/ProfileController.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Web.Mvc; 3 | using BaseLibrary; 4 | using DataModels; 5 | using u2flib.Util; 6 | 7 | namespace DemoU2FSite.Controllers 8 | { 9 | [Authorize] 10 | [LegacyAuthorize] 11 | public class ProfileController : Controller 12 | { 13 | private readonly IUserRepository _userRepository; 14 | private readonly IMemberShipService _memberShipService; 15 | 16 | public ProfileController(IUserRepository userRepository, IMemberShipService memberShipService) 17 | { 18 | _userRepository = userRepository; 19 | _memberShipService = memberShipService; 20 | } 21 | 22 | public ActionResult Index() 23 | { 24 | if (!HttpContext.User.Identity.IsAuthenticated) 25 | { 26 | ModelState.AddModelError("", "User has timed out."); 27 | RedirectToAction("Login", "Home"); 28 | } 29 | var user = _userRepository.FindUser(HttpContext.User.Identity.Name); 30 | return View("Index", user); 31 | } 32 | 33 | public void AddDevice(string deviceResponse) 34 | { 35 | if (!HttpContext.User.Identity.IsAuthenticated) 36 | { 37 | ModelState.AddModelError("", "User has timed out."); 38 | RedirectToAction("Login", "Home"); 39 | } 40 | _memberShipService.CompleteRegistration(HttpContext.User.Identity.Name, deviceResponse); 41 | } 42 | 43 | public JsonResult GetChallenge() 44 | { 45 | ServerRegisterResponse serverRegisterResponse = _memberShipService.GenerateServerChallenge(HttpContext.User.Identity.Name); 46 | CompleteRegisterModel registerModel = new CompleteRegisterModel 47 | { 48 | UserName = HttpContext.User.Identity.Name, 49 | AppId = serverRegisterResponse.AppId, 50 | Challenge = serverRegisterResponse.Challenge, 51 | Version = serverRegisterResponse.Version 52 | }; 53 | 54 | JsonResult result = new JsonResult 55 | { 56 | Data = registerModel 57 | }; 58 | return result; 59 | } 60 | 61 | public JsonResult DeviceInfo(int deviceId) 62 | { 63 | if (!HttpContext.User.Identity.IsAuthenticated) 64 | { 65 | ModelState.AddModelError("", "User has timed out."); 66 | RedirectToAction("Login", "Home"); 67 | } 68 | 69 | var user = _userRepository.FindUser(HttpContext.User.Identity.Name); 70 | var device = user.DeviceRegistrations.FirstOrDefault(f => f.Id == deviceId); 71 | dynamic formattedResult = new 72 | { 73 | Id = device.Id, 74 | KeyHandle = Utils.ByteArrayToBase64String(device.KeyHandle), 75 | PublicKey = Utils.ByteArrayToBase64String(device.PublicKey), 76 | Counter = device.Counter, 77 | UpdatedOn = device.UpdatedOn.ToShortDateString() 78 | }; 79 | JsonResult result = new JsonResult 80 | { 81 | Data = formattedResult 82 | }; 83 | return result; 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /UnitTests/U2F/Messages/RegisterResponseUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using u2flib.Data.Messages; 3 | 4 | namespace UnitTests.Messages 5 | { 6 | [TestClass] 7 | public class RegisterResponseUnitTests 8 | { 9 | private const string JsonData = "{\"RegistrationData\":\"BQSxdLxJx8olS3DS5cIHzunPF0gg69d+o8ZVCMJtpRtlfBzGuVL4YhaXk2SC2gptPTgmpZCV2vbNfAPi5gOF0vbZQCpVLf23R37WX9hBM/hhlgELIhW1faddMVt7no/i45JaYBlVG6th0WWRZZy68AtJUPer/mZg4uAG92hot3LXDCUwggE8MIHkoAMCAQICCkeQEoAAEVWVc1IwCgYIKoZIzj0EAwIwFzEVMBMGA1UEAxMMR251YmJ5IFBpbG90MB4XDTEyMDgxNDE4MjkzMloXDTEzMDgxNDE4MjkzMlowMTEvMC0GA1UEAxMmUGlsb3RHbnViYnktMC40LjEtNDc5MDEyODAwMDExNTU5NTczNTIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASNYX5lyVCOZLzFZzrIKmeZ2jwURmgsJYxGP//fWN/S+j5sN4tT15XEpN/7QZnt14YvI6uvAgO0uJEboFaZlOEBMAoGCCqGSM49BAMCA0cAMEQCIGDNtgYenCImLRqsHZbYxwgpsjZlMd2iaIMsuDa80w36AiBjGxRZ8J5jMAVXIsjYm39IiDuQibiNYNHZeVkCswQQ3zBFAiAUcYmbzDmH5i6CAsmznDPBkDP3NANS26gPyrAX25Iw5AIhAIJnfWc9iRkzreb2F+Xb3i4kfnBCP9WteASm09OWHvhx\",\"ClientData\":\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6InZxclM2V1hEZTFKVXM1X2MzaTQtTGtLSUhSci0zWFZiM2F6dUE1VGlmSG8iLCJjaWRfcHVia2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiSHpRd2xmWFg3UTRTNU10Q0NuWlVOQnczUk16UE85dE95V2pCcVJsNHRKOCIsInkiOiJYVmd1R0ZMSVp4MWZYZzN3TnFmZGJuNzVoaTQtXzctQnhoTWxqdzQySHQ0In0sIm9yaWdpbiI6Imh0dHA6Ly9leGFtcGxlLmNvbSJ9\"}"; 10 | 11 | [TestMethod] 12 | public void RegisterResponse_ConstructsProperly() 13 | { 14 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 15 | 16 | Assert.IsNotNull(registerResponse); 17 | Assert.IsNotNull(registerResponse.GetClientData()); 18 | Assert.IsNotNull(registerResponse.ToJson()); 19 | Assert.IsTrue(registerResponse.GetHashCode() != 0); 20 | Assert.AreEqual(JsonData, registerResponse.ToJson()); 21 | Assert.AreEqual(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, registerResponse.RegistrationData); 22 | Assert.AreEqual(TestConts.CLIENT_DATA_REGISTER_BASE64, registerResponse.ClientData); 23 | } 24 | 25 | [TestMethod] 26 | public void RegisterResponse_FromJson() 27 | { 28 | RegisterResponse registerResponse = RegisterResponse.FromJson(JsonData); 29 | 30 | Assert.IsNotNull(registerResponse); 31 | Assert.IsNotNull(registerResponse.GetClientData()); 32 | Assert.IsNotNull(registerResponse.ToJson()); 33 | Assert.IsTrue(registerResponse.GetHashCode() != 0); 34 | Assert.AreEqual(JsonData, registerResponse.ToJson()); 35 | Assert.AreEqual(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, registerResponse.RegistrationData); 36 | Assert.AreEqual(TestConts.CLIENT_DATA_REGISTER_BASE64, registerResponse.ClientData); 37 | } 38 | 39 | [TestMethod] 40 | public void RegisterResponse_Equals() 41 | { 42 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 43 | RegisterResponse sameRegisterResponse = RegisterResponse.FromJson(JsonData); 44 | 45 | Assert.IsTrue(sameRegisterResponse.Equals(registerResponse)); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Services/Services.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5D7F3587-39D8-4CC1-B693-049327688022} 8 | Library 9 | Properties 10 | Services 11 | Services 12 | v4.6.1 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {DCCEF63C-D4AD-4A0D-AFB8-C4AE1AC693DC} 51 | BaseLibrary 52 | 53 | 54 | {152FABFB-DEC9-412C-AF1C-D3231EA4AF3D} 55 | DataModels 56 | 57 | 58 | {A58F53C9-A51D-4548-A70C-3A08AA635A71} 59 | Repositories 60 | 61 | 62 | {98777070-04C9-442D-8E60-4E0B1E7FCDBF} 63 | u2flib 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /BaseLibrary/BaseLibrary.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DCCEF63C-D4AD-4A0D-AFB8-C4AE1AC693DC} 8 | Library 9 | Properties 10 | BaseLibrary 11 | BaseLibrary 12 | v4.6.1 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 38 | True 39 | 40 | 41 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 42 | True 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {152fabfb-dec9-412c-af1c-d3231ea4af3d} 62 | DataModels 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | -------------------------------------------------------------------------------- /DemoU2FSite/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @using System.Web.Optimization 3 | 4 | 5 | 6 | 7 | ASP.NET MVC Application U2F Demo 8 | 9 | 10 | @Styles.Render("~/Content/bootstrap") 11 | 12 | @RenderSection("specialJS", required: false) 13 | 14 | 15 | 16 | 17 | 36 | 37 |
38 |
39 |
40 |
41 | 44 |
45 | 55 | 56 | @RenderSection("featured", required: false) 57 | @RenderBody() 58 |
59 | 60 |
61 | 62 |
63 |
64 |

© @DateTime.Now.Year - ASP.NET MVC Application U2F Demo

65 |
66 |

This demo site is running version @typeof(u2flib.U2F).Assembly.GetName().Version of the U2F library.

67 |
68 |
69 | 70 | @Scripts.Render("~/bundles/jquery") 71 | @Scripts.Render("~/bundles/browser") 72 | @Scripts.Render("~/bundles/bootstrap") 73 | 74 | @RenderSection("scripts", required: false) 75 | 76 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /u2flib/Crypto/BouncyCastleCrypto.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Security.Cryptography; 15 | using Org.BouncyCastle.Asn1; 16 | using Org.BouncyCastle.Asn1.Sec; 17 | using Org.BouncyCastle.Asn1.X9; 18 | using Org.BouncyCastle.Crypto; 19 | using Org.BouncyCastle.Crypto.Parameters; 20 | using Org.BouncyCastle.Math.EC; 21 | using Org.BouncyCastle.Security; 22 | using Org.BouncyCastle.X509; 23 | using u2flib.Exceptions; 24 | using u2flib.Util; 25 | 26 | namespace u2flib.Crypto 27 | { 28 | public class BouncyCastleCrypto : ICrypto 29 | { 30 | private readonly DerObjectIdentifier _curve = SecObjectIdentifiers.SecP256r1; 31 | private readonly SHA256Managed _sha256 = new SHA256Managed(); 32 | private const string SignatureError = "Error when verifying signature"; 33 | private const string ErrorDecodingPublicKey = "Error when decoding public key"; 34 | 35 | public bool CheckSignature(X509Certificate attestationCertificate, byte[] signedBytes, byte[] signature) 36 | { 37 | return CheckSignature(attestationCertificate.GetPublicKey(), signedBytes, signature); 38 | } 39 | 40 | public bool CheckSignature(ICipherParameters getPublicKey, byte[] signedBytes, byte[] signature) 41 | { 42 | try 43 | { 44 | ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA"); 45 | signer.Init(false, getPublicKey); 46 | signer.BlockUpdate(signedBytes, 0, signedBytes.Length); 47 | 48 | if (!signer.VerifySignature(signature)) 49 | throw new U2fException(SignatureError); 50 | 51 | return true; 52 | } 53 | catch (InvalidKeyException e) 54 | { 55 | throw new U2fException(SignatureError, e); 56 | } 57 | catch (SignatureException e) 58 | { 59 | throw new U2fException(SignatureError, e); 60 | } 61 | } 62 | 63 | public ICipherParameters DecodePublicKey(byte[] encodedPublicKey) 64 | { 65 | try 66 | { 67 | X9ECParameters curve = SecNamedCurves.GetByOid(_curve); 68 | ECPoint point = curve.Curve.DecodePoint(encodedPublicKey); 69 | ECDomainParameters ecP = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H); 70 | 71 | return new ECPublicKeyParameters(point, ecP); 72 | } 73 | catch (InvalidKeySpecException e) 74 | { 75 | throw new U2fException(ErrorDecodingPublicKey, e); 76 | } 77 | catch (Exception e) 78 | { 79 | throw new U2fException("Could not parse user public key", e); 80 | } 81 | } 82 | 83 | public byte[] Hash(byte[] bytes) 84 | { 85 | try 86 | { 87 | byte[] hash = _sha256.ComputeHash(bytes); 88 | 89 | return hash; 90 | } 91 | catch (Exception e) 92 | { 93 | throw new UnsupportedOperationException("Error when computing SHA-256", e); 94 | } 95 | } 96 | 97 | public byte[] Hash(string str) 98 | { 99 | return Hash(Utils.GetBytes(str)); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /DemoU2FSite/Scripts/jquery.unobtrusive-ajax.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive Ajax support library for jQuery 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var b="unobtrusiveAjaxClick",d="unobtrusiveAjaxClickTarget",h="unobtrusiveValidation";function c(d,b){var a=window,c=(d||"").split(".");while(a&&c.length)a=a[c.shift()];if(typeof a==="function")return a;b.push(d);return Function.constructor.apply(null,b)}function e(a){return a==="GET"||a==="POST"}function g(b,a){!e(a)&&b.setRequestHeader("X-HTTP-Method-Override",a)}function i(c,b,e){var d;if(e.indexOf("application/x-javascript")!==-1)return;d=(c.getAttribute("data-ajax-mode")||"").toUpperCase();a(c.getAttribute("data-ajax-update")).each(function(f,c){var e;switch(d){case"BEFORE":e=c.firstChild;a("
").html(b).contents().each(function(){c.insertBefore(this,e)});break;case"AFTER":a("
").html(b).contents().each(function(){c.appendChild(this)});break;case"REPLACE-WITH":a(c).replaceWith(b);break;default:a(c).html(b)}})}function f(b,d){var j,k,f,h;j=b.getAttribute("data-ajax-confirm");if(j&&!window.confirm(j))return;k=a(b.getAttribute("data-ajax-loading"));h=parseInt(b.getAttribute("data-ajax-loading-duration"),10)||0;a.extend(d,{type:b.getAttribute("data-ajax-method")||undefined,url:b.getAttribute("data-ajax-url")||undefined,cache:!!b.getAttribute("data-ajax-cache"),beforeSend:function(d){var a;g(d,f);a=c(b.getAttribute("data-ajax-begin"),["xhr"]).apply(b,arguments);a!==false&&k.show(h);return a},complete:function(){k.hide(h);c(b.getAttribute("data-ajax-complete"),["xhr","status"]).apply(b,arguments)},success:function(a,e,d){i(b,a,d.getResponseHeader("Content-Type")||"text/html");c(b.getAttribute("data-ajax-success"),["data","status","xhr"]).apply(b,arguments)},error:function(){c(b.getAttribute("data-ajax-failure"),["xhr","status","error"]).apply(b,arguments)}});d.data.push({name:"X-Requested-With",value:"XMLHttpRequest"});f=d.type.toUpperCase();if(!e(f)){d.type="POST";d.data.push({name:"X-HTTP-Method-Override",value:f})}a.ajax(d)}function j(c){var b=a(c).data(h);return!b||!b.validate||b.validate()}a(document).on("click","a[data-ajax=true]",function(a){a.preventDefault();f(this,{url:this.href,type:"GET",data:[]})});a(document).on("click","form[data-ajax=true] input[type=image]",function(c){var g=c.target.name,e=a(c.target),f=a(e.parents("form")[0]),d=e.offset();f.data(b,[{name:g+".x",value:Math.round(c.pageX-d.left)},{name:g+".y",value:Math.round(c.pageY-d.top)}]);setTimeout(function(){f.removeData(b)},0)});a(document).on("click","form[data-ajax=true] :submit",function(e){var g=e.currentTarget.name,f=a(e.target),c=a(f.parents("form")[0]);c.data(b,g?[{name:g,value:e.currentTarget.value}]:[]);c.data(d,f);setTimeout(function(){c.removeData(b);c.removeData(d)},0)});a(document).on("submit","form[data-ajax=true]",function(h){var e=a(this).data(b)||[],c=a(this).data(d),g=c&&c.hasClass("cancel");h.preventDefault();if(!g&&!j(this))return;f(this,{url:this.action,type:this.method||"GET",data:e.concat(a(this).serializeArray())})})})(jQuery); -------------------------------------------------------------------------------- /u2flib/Data/Messages/StartedRegistration.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Linq; 15 | 16 | namespace u2flib.Data.Messages 17 | { 18 | public class StartedRegistration : DataObject 19 | { 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The challenge. 24 | /// The application identifier. 25 | public StartedRegistration(String challenge, String appId) 26 | { 27 | Version = U2F.U2FVersion; 28 | Challenge = challenge; 29 | AppId = appId; 30 | } 31 | 32 | /// 33 | /// Gets or sets the version. 34 | /// Version of the protocol that the to-be-registered U2F token must speak. For 35 | /// the version of the protocol described herein, must be "U2F_V2" 36 | /// 37 | /// 38 | /// The version. 39 | /// 40 | public String Version { get; private set; } 41 | 42 | /// 43 | /// Gets the challenge. 44 | /// The websafe-base64-encoded challenge. 45 | /// 46 | /// 47 | /// The challenge. 48 | /// 49 | public String Challenge { get; private set; } 50 | 51 | /// 52 | /// Gets the application identifier. 53 | /// The application id that the RP would like to assert. The U2F token will 54 | /// enforce that the key handle provided above is associated with this 55 | /// application id. The browser enforces that the calling origin belongs to the 56 | /// application identified by the application id. 57 | /// 58 | /// 59 | /// The application identifier. 60 | /// 61 | public String AppId { get; private set; } 62 | 63 | public override int GetHashCode() 64 | { 65 | int hash = Version.Sum(c => c + 31); 66 | hash += Challenge.Sum(c => c + 31); 67 | hash += AppId.Sum(c => c + 31); 68 | 69 | return hash; 70 | } 71 | 72 | public override bool Equals(Object obj) 73 | { 74 | if (this == obj) 75 | return true; 76 | if (obj == null) 77 | return false; 78 | if (GetType() != obj.GetType()) 79 | return false; 80 | StartedRegistration other = (StartedRegistration) obj; 81 | if (AppId == null) 82 | { 83 | if (other.AppId != null) 84 | return false; 85 | } 86 | else if (!AppId.Equals(other.AppId)) 87 | return false; 88 | if (Challenge == null) 89 | { 90 | if (other.Challenge != null) 91 | return false; 92 | } 93 | else if (!Challenge.Equals(other.Challenge)) 94 | return false; 95 | if (Version == null) 96 | { 97 | if (other.Version != null) 98 | return false; 99 | } 100 | else if (!Version.Equals(other.Version)) 101 | return false; 102 | return true; 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /u2flib/Data/Messages/AuthenticateResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Linq; 15 | 16 | namespace u2flib.Data.Messages 17 | { 18 | public class AuthenticateResponse : DataObject 19 | { 20 | private readonly ClientData _clientDataRef; 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The client data. 26 | /// The signature data. 27 | /// The key handle. 28 | public AuthenticateResponse(string clientData, string signatureData, string keyHandle) 29 | { 30 | ClientData = clientData; 31 | SignatureData = signatureData; 32 | KeyHandle = keyHandle; 33 | _clientDataRef = new ClientData(ClientData); 34 | } 35 | 36 | public ClientData GetClientData() 37 | { 38 | return _clientDataRef; 39 | } 40 | 41 | public String GetRequestId() 42 | { 43 | return GetClientData().Challenge; 44 | } 45 | 46 | /// 47 | /// Gets the signature data. 48 | /// websafe-base64(raw response from U2F device) 49 | /// 50 | /// 51 | /// The signature data. 52 | /// 53 | public String SignatureData { get; private set; } 54 | 55 | /// 56 | /// Gets the Client data. 57 | /// 58 | /// 59 | /// The Client data. 60 | /// 61 | public String ClientData { get; private set; } 62 | 63 | /// 64 | /// keyHandle originally passed 65 | /// 66 | /// 67 | /// The key handle. 68 | /// 69 | public String KeyHandle { get; private set; } 70 | 71 | public override int GetHashCode() 72 | { 73 | int hash = ClientData.Sum(c => c + 31); 74 | hash += SignatureData.Sum(c => c + 31); 75 | hash += KeyHandle.Sum(c => c + 31); 76 | 77 | return hash; 78 | } 79 | 80 | public override bool Equals(Object obj) 81 | { 82 | if (this == obj) 83 | return true; 84 | if (obj == null) 85 | return false; 86 | if (GetType() != obj.GetType()) 87 | return false; 88 | 89 | AuthenticateResponse other = (AuthenticateResponse) obj; 90 | 91 | if (ClientData == null) 92 | { 93 | if (other.ClientData != null) 94 | return false; 95 | } 96 | else if (!ClientData.Equals(other.ClientData)) 97 | return false; 98 | 99 | if (KeyHandle == null) 100 | { 101 | if (other.KeyHandle != null) 102 | return false; 103 | } 104 | else if (!KeyHandle.Equals(other.KeyHandle)) 105 | return false; 106 | 107 | if (SignatureData == null) 108 | { 109 | if (other.SignatureData != null) 110 | return false; 111 | } 112 | else if (!SignatureData.Equals(other.SignatureData)) 113 | return false; 114 | 115 | return true; 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Repositories/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using BaseLibrary; 5 | using DataModels; 6 | 7 | namespace Repositories 8 | { 9 | public class UserRepository : IUserRepository 10 | { 11 | private readonly IDataContext _dataContext; 12 | 13 | public UserRepository(IDataContext context) 14 | { 15 | _dataContext = context; 16 | } 17 | 18 | public User FindUser(string userName) 19 | { 20 | return _dataContext.Users.FirstOrDefault(f => f.Name.Equals(userName)); 21 | } 22 | 23 | public void UpdateDeviceCounter(string userName, byte[] devicePublicKey, uint counter) 24 | { 25 | User user = _dataContext.Users.FirstOrDefault(f => f.Name.Equals(userName.Trim())); 26 | 27 | if (user != null) 28 | { 29 | Device device = user.DeviceRegistrations.FirstOrDefault(w => w.PublicKey.Equals(devicePublicKey)); 30 | 31 | if (device != null) 32 | { 33 | device.Counter = Convert.ToInt32(counter); 34 | device.UpdatedOn = DateTime.Now; 35 | 36 | _dataContext.SaveChanges(); 37 | } 38 | } 39 | } 40 | 41 | public void RemoveUsersAuthenticationRequests(string userName) 42 | { 43 | User user = _dataContext.Users.FirstOrDefault(f => f.Name.Equals(userName.Trim())); 44 | 45 | if (user != null) 46 | { 47 | user.AuthenticationRequest.Clear(); 48 | user.UpdatedOn = DateTime.Now; 49 | 50 | _dataContext.SaveChanges(); 51 | } 52 | } 53 | 54 | public void SaveUserAuthenticationRequest(string userName, string appId, string challenge, string keyHandle) 55 | { 56 | User user = _dataContext.Users.FirstOrDefault(f => f.Name.Equals(userName.Trim())); 57 | 58 | if(user == null) 59 | return; 60 | 61 | if (user.AuthenticationRequest == null) 62 | user.AuthenticationRequest = new List(); 63 | 64 | user.AuthenticationRequest.Add( 65 | new AuthenticationRequest 66 | { 67 | AppId = appId, 68 | Challenge = challenge, 69 | KeyHandle = keyHandle 70 | }); 71 | 72 | user.UpdatedOn = DateTime.Now; 73 | _dataContext.SaveChanges(); 74 | } 75 | 76 | public void AddUser(string userName, string hashedPassword) 77 | { 78 | _dataContext.Users.Add(new User 79 | { 80 | Name = userName, 81 | Password = hashedPassword, 82 | CreatedOn = DateTime.Now, 83 | UpdatedOn = DateTime.Now 84 | }); 85 | 86 | _dataContext.SaveChanges(); 87 | } 88 | 89 | public void AddDeviceRegistration(string userName, byte[] attestationCert, uint counter, byte[] keyHandle, byte[] publicKey) 90 | { 91 | User user = _dataContext.Users.FirstOrDefault(f => f.Name.Equals(userName)); 92 | 93 | if (user == null) 94 | return; 95 | 96 | user.UpdatedOn = DateTime.Now; 97 | user.AuthenticationRequest.Clear(); 98 | user.DeviceRegistrations.Add(new Device 99 | { 100 | AttestationCert = attestationCert, 101 | Counter = Convert.ToInt32(counter), 102 | CreatedOn = DateTime.Now, 103 | UpdatedOn = DateTime.Now, 104 | KeyHandle = keyHandle, 105 | PublicKey = publicKey 106 | }); 107 | 108 | _dataContext.SaveChanges(); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 3-clause -> 2-clause 2 | 3 | Copyright (c) 2015, Bryce Foster 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | 12 | ------------------------------- 13 | 14 | Copyright (c) 2014, Yubico AB 15 | All rights reserved. 16 | 17 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 18 | 19 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 20 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | ------------------------------- 24 | 25 | Copyright (c) 2014, Google Inc. 26 | All rights reserved. 27 | 28 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 29 | 30 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 31 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 32 | Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 33 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /UnitTests/U2F/U2FUnitTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Org.BouncyCastle.X509; 3 | using u2flib; 4 | using u2flib.Data; 5 | using u2flib.Data.Messages; 6 | using u2flib.Util; 7 | 8 | namespace UnitTests 9 | { 10 | [TestClass] 11 | public class U2FUnitTests 12 | { 13 | 14 | [TestMethod] 15 | public void U2F_FinishRegistration() 16 | { 17 | StartedRegistration startedRegistration = new StartedRegistration(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, TestConts.APP_ID_ENROLL); 18 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 19 | 20 | DeviceRegistration results = U2F.FinishRegistration(startedRegistration, registerResponse, TestConts.TRUSTED_DOMAINS); 21 | 22 | Assert.IsNotNull(results); 23 | Assert.IsNotNull(results.KeyHandle); 24 | Assert.IsNotNull(results.PublicKey); 25 | Assert.IsNotNull(results.GetAttestationCertificate()); 26 | } 27 | 28 | [TestMethod] 29 | public void U2F_FinishRegistrationNoFacets() 30 | { 31 | StartedRegistration startedRegistration = new StartedRegistration(TestConts.SERVER_CHALLENGE_REGISTER_BASE64, TestConts.APP_ID_ENROLL); 32 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 33 | 34 | var results = U2F.FinishRegistration(startedRegistration, registerResponse); 35 | 36 | Assert.IsNotNull(results); 37 | Assert.IsNotNull(results.KeyHandle); 38 | Assert.IsNotNull(results.PublicKey); 39 | Assert.IsNotNull(results.GetAttestationCertificate()); 40 | } 41 | 42 | [TestMethod] 43 | public void U2F_StartRegistration() 44 | { 45 | var results = U2F.StartRegistration(TestConts.APP_ID_ENROLL); 46 | 47 | Assert.IsNotNull(results); 48 | Assert.IsNotNull(results.Challenge); 49 | Assert.IsNotNull(results.Version); 50 | Assert.AreEqual(results.AppId, TestConts.APP_ID_ENROLL); 51 | } 52 | 53 | [TestMethod] 54 | public void U2F_StartAuthentication() 55 | { 56 | RegisterResponse registerResponse = new RegisterResponse(TestConts.REGISTRATION_RESPONSE_DATA_BASE64, TestConts.CLIENT_DATA_REGISTER_BASE64); 57 | RawRegisterResponse rawAuthenticateResponse = RawRegisterResponse.FromBase64(registerResponse.RegistrationData); 58 | DeviceRegistration deviceRegistration = rawAuthenticateResponse.CreateDevice(); 59 | 60 | var results = U2F.StartAuthentication(TestConts.APP_ID_ENROLL, deviceRegistration); 61 | 62 | Assert.IsNotNull(results); 63 | Assert.IsNotNull(results.AppId); 64 | Assert.IsNotNull(results.Challenge); 65 | Assert.IsNotNull(results.KeyHandle); 66 | Assert.IsNotNull(results.Version); 67 | } 68 | 69 | [TestMethod] 70 | public void U2F_FinishAuthentication() 71 | { 72 | StartedAuthentication startedAuthentication = new StartedAuthentication( 73 | TestConts.SERVER_CHALLENGE_SIGN_BASE64, 74 | TestConts.APP_SIGN_ID, 75 | TestConts.KEY_HANDLE_BASE64); 76 | 77 | AuthenticateResponse authenticateResponse = new AuthenticateResponse( 78 | TestConts.CLIENT_DATA_AUTHENTICATE_BASE64, 79 | TestConts.SIGN_RESPONSE_DATA_BASE64, 80 | TestConts.KEY_HANDLE_BASE64); 81 | 82 | 83 | DeviceRegistration deviceRegistration = new DeviceRegistration(TestConts.KEY_HANDLE_BASE64_BYTE, TestConts.USER_PUBLIC_KEY_AUTHENTICATE_HEX, 84 | Utils.Base64StringToByteArray(TestConts.ATTESTATION_CERTIFICATE), 0); 85 | 86 | uint orginalValue = deviceRegistration.Counter; 87 | 88 | U2F.FinishAuthentication(startedAuthentication, authenticateResponse, deviceRegistration); 89 | 90 | Assert.IsTrue(deviceRegistration.Counter != 0); 91 | Assert.AreNotEqual(orginalValue, deviceRegistration.Counter); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /UnitTests/TestConts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using u2flib.Util; 4 | 5 | namespace UnitTests 6 | { 7 | public static class TestConts 8 | { 9 | //Test vectors from FIDO U2F: Raw Message Formats - Draft 4 10 | public static HashSet TRUSTED_DOMAINS = new HashSet() { "http://example.com" }; 11 | public static String APP_ID_ENROLL = "http://example.com"; 12 | public static String APP_SIGN_ID = "https://gstatic.com/securitykey/a/example.com"; 13 | public static String ORIGIN = "http://example.com"; 14 | 15 | public static String SERVER_CHALLENGE_REGISTER_BASE64 = "vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo"; 16 | 17 | public static String SERVER_CHALLENGE_SIGN_BASE64 = "opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o"; 18 | 19 | public static String CHANNEL_ID_STRING = 20 | "{\"kty\":\"EC\"," 21 | + "\"crv\":\"P-256\"," 22 | + "\"x\":\"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8\"," 23 | + "\"y\":\"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4\"}"; 24 | 25 | public static String CLIENT_DATA_REGISTER = 26 | "{\"typ\":\"navigator.id.finishEnrollment\",\"challenge\":\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\",\"origin\":\"http://example.com\",\"cid_pubkey\":\"BNNo8bZlut48M6IPHkKcd1DVAzZgwBkRnSmqS6erwEqnyApGu-EcqMtWdNdPMfipA_a60QX7ardK7-9NuLACXh0\"}"; 27 | 28 | 29 | public static String CLIENT_DATA_REGISTER_BASE64 = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6InZxclM2V1hEZTFKVXM1X2MzaTQtTGtLSUhSci0zWFZiM2F6dUE1VGlmSG8iLCJjaWRfcHVia2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiSHpRd2xmWFg3UTRTNU10Q0NuWlVOQnczUk16UE85dE95V2pCcVJsNHRKOCIsInkiOiJYVmd1R0ZMSVp4MWZYZzN3TnFmZGJuNzVoaTQtXzctQnhoTWxqdzQySHQ0In0sIm9yaWdpbiI6Imh0dHA6Ly9leGFtcGxlLmNvbSJ9"; 30 | 31 | public static String CLIENT_DATA_AUTHENTICATE = "{\"typ\":\"navigator.id.getAssertion\"," + "\"challenge\":\"" + SERVER_CHALLENGE_SIGN_BASE64 32 | + "\"," + "\"cid_pubkey\":" + CHANNEL_ID_STRING + "," + "\"origin\":\"" + ORIGIN + "\"}"; 33 | 34 | public static String CLIENT_DATA_AUTHENTICATE_BASE64 = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0"; 35 | 36 | 37 | public static String REGISTRATION_RESPONSE_DATA_BASE64 ="BQSxdLxJx8olS3DS5cIHzunPF0gg69d+o8ZVCMJtpRtlfBzGuVL4YhaXk2SC2gptPTgmpZCV2vbNfAPi5gOF0vbZQCpVLf23R37WX9hBM/hhlgELIhW1faddMVt7no/i45JaYBlVG6th0WWRZZy68AtJUPer/mZg4uAG92hot3LXDCUwggE8MIHkoAMCAQICCkeQEoAAEVWVc1IwCgYIKoZIzj0EAwIwFzEVMBMGA1UEAxMMR251YmJ5IFBpbG90MB4XDTEyMDgxNDE4MjkzMloXDTEzMDgxNDE4MjkzMlowMTEvMC0GA1UEAxMmUGlsb3RHbnViYnktMC40LjEtNDc5MDEyODAwMDExNTU5NTczNTIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASNYX5lyVCOZLzFZzrIKmeZ2jwURmgsJYxGP//fWN/S+j5sN4tT15XEpN/7QZnt14YvI6uvAgO0uJEboFaZlOEBMAoGCCqGSM49BAMCA0cAMEQCIGDNtgYenCImLRqsHZbYxwgpsjZlMd2iaIMsuDa80w36AiBjGxRZ8J5jMAVXIsjYm39IiDuQibiNYNHZeVkCswQQ3zBFAiAUcYmbzDmH5i6CAsmznDPBkDP3NANS26gPyrAX25Iw5AIhAIJnfWc9iRkzreb2F+Xb3i4kfnBCP9WteASm09OWHvhx"; 38 | 39 | public static string KEY_HANDLE_BASE64 = "KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ"; 40 | 41 | public static byte[] KEY_HANDLE_BASE64_BYTE = Utils.Base64StringToByteArray(KEY_HANDLE_BASE64); 42 | 43 | 44 | public static byte[] USER_PUBLIC_KEY_AUTHENTICATE_HEX = Utils.Base64StringToByteArray("BNNo8bZlut48M6IPHkKcd1DVAzZgwBkRnSmqS6erwEqnyApGu-EcqMtWdNdPMfipA_a60QX7ardK7-9NuLACXh0"); 45 | 46 | 47 | public static String SIGN_RESPONSE_DATA_BASE64 = "AQAAAAEwRAIgS18M0XU0zt2MNO4JVw71QqNT30Q2AwzkPUBt6HC4R3gCICZ7uZj6ybcmbrYOfLC16r39W6lhT1PHsiJy7BAEepI_"; 48 | 49 | public static String ATTESTATION_CERTIFICATE = "MIIBPDCB5KADAgECAgpHkBKAABFVlXNSMAoGCCqGSM49BAMCMBcxFTATBgNVBAMTDEdudWJieSBQaWxvdDAeFw0xMjA4MTQxODI5MzJaFw0xMzA4MTQxODI5MzJaMDExLzAtBgNVBAMTJlBpbG90R251YmJ5LTAuNC4xLTQ3OTAxMjgwMDAxMTU1OTU3MzUyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjWF-ZclQjmS8xWc6yCpnmdo8FEZoLCWMRj__31jf0vo-bDeLU9eVxKTf-0GZ7deGLyOrrwIDtLiRG6BWmZThATAKBggqhkjOPQQDAgNHADBEAiBgzbYGHpwiJi0arB2W2McIKbI2ZTHdomiDLLg2vNMN-gIgYxsUWfCeYzAFVyLI2Jt_SIg7kIm4jWDR2XlZArMEEN8"; 50 | } 51 | } -------------------------------------------------------------------------------- /DataModels/DataModels.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {152FABFB-DEC9-412C-AF1C-D3231EA4AF3D} 8 | Library 9 | Properties 10 | DataModels 11 | DataModels 12 | v4.6.1 13 | 512 14 | ..\ 15 | true 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | false 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 40 | True 41 | 42 | 43 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 44 | True 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 73 | 74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /UnitTests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | *.userprefs 9 | .vs/ 10 | .vscode/ 11 | 12 | # Build results 13 | [Dd]ebug/ 14 | [Dd]ebugPublic/ 15 | [Rr]elease/ 16 | [Rr]eleases/ 17 | x64/ 18 | x86/ 19 | build/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Roslyn cache directories 25 | *.ide/ 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | #NUNIT 32 | *.VisualState.xml 33 | TestResult.xml 34 | 35 | # Build Results of an ATL Project 36 | [Dd]ebugPS/ 37 | [Rr]eleasePS/ 38 | dlldata.c 39 | 40 | *_i.c 41 | *_p.c 42 | *_i.h 43 | *.ilk 44 | *.meta 45 | *.obj 46 | *.pch 47 | *.pdb 48 | *.pgc 49 | *.pgd 50 | *.rsp 51 | *.sbr 52 | *.tlb 53 | *.tli 54 | *.tlh 55 | *.tmp 56 | *.tmp_proj 57 | *.log 58 | *.vspscc 59 | *.vssscc 60 | .builds 61 | *.pidb 62 | *.svclog 63 | *.scc 64 | 65 | # Chutzpah Test files 66 | _Chutzpah* 67 | 68 | # Visual C++ cache files 69 | ipch/ 70 | *.aps 71 | *.ncb 72 | *.opensdf 73 | *.sdf 74 | *.cachefile 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | *.vspx 80 | 81 | # TFS 2012 Local Workspace 82 | $tf/ 83 | 84 | # Guidance Automation Toolkit 85 | *.gpState 86 | 87 | # ReSharper is a .NET coding add-in 88 | _ReSharper*/ 89 | *.[Rr]e[Ss]harper 90 | *.DotSettings.user 91 | 92 | # JustCode is a .NET coding addin-in 93 | .JustCode 94 | 95 | # TeamCity is a build add-in 96 | _TeamCity* 97 | 98 | # DotCover is a Code Coverage Tool 99 | *.dotCover 100 | 101 | # NCrunch 102 | _NCrunch_* 103 | .*crunch*.local.xml 104 | 105 | # MightyMoose 106 | *.mm.* 107 | AutoTest.Net/ 108 | 109 | # Web workbench (sass) 110 | .sass-cache/ 111 | 112 | # Installshield output folder 113 | [Ee]xpress/ 114 | 115 | # DocProject is a documentation generator add-in 116 | DocProject/buildhelp/ 117 | DocProject/Help/*.HxT 118 | DocProject/Help/*.HxC 119 | DocProject/Help/*.hhc 120 | DocProject/Help/*.hhk 121 | DocProject/Help/*.hhp 122 | DocProject/Help/Html2 123 | DocProject/Help/html 124 | 125 | # Click-Once directory 126 | publish/ 127 | 128 | # Publish Web Output 129 | *.[Pp]ublish.xml 130 | *.azurePubxml 131 | # TODO: Comment the next line if you want to checkin your web deploy settings 132 | # but database connection strings (with potential passwords) will be unencrypted 133 | *.pubxml 134 | *.publishproj 135 | 136 | # NuGet Packages 137 | *.nupkg 138 | # The packages folder can be ignored because of Package Restore 139 | **/packages/* 140 | # except build/, which is used as an MSBuild target. 141 | !**/packages/build/ 142 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 143 | #!**/packages/repositories.config 144 | 145 | # Windows Azure Build Output 146 | csx/ 147 | *.build.csdef 148 | 149 | # Windows Store app package directory 150 | AppPackages/ 151 | 152 | # Others 153 | sql/ 154 | *.Cache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | 165 | # RIA/Silverlight projects 166 | Generated_Code/ 167 | 168 | # Backup & report files from converting an old project file 169 | # to a newer Visual Studio version. Backup files are not needed, 170 | # because we have git ;-) 171 | _UpgradeReport_Files/ 172 | Backup*/ 173 | UpgradeLog*.XML 174 | UpgradeLog*.htm 175 | 176 | # SQL Server files 177 | *.mdf 178 | *.ldf 179 | 180 | # Business Intelligence projects 181 | *.rdl.data 182 | *.bim.layout 183 | *.bim_*.settings 184 | 185 | # Microsoft Fakes 186 | FakesAssemblies/ 187 | 188 | # ========================= 189 | # Operating System Files 190 | # ========================= 191 | 192 | # OSX 193 | # ========================= 194 | 195 | .DS_Store 196 | .AppleDouble 197 | .LSOverride 198 | 199 | # Icon must end with two \r 200 | Icon 201 | 202 | 203 | # Thumbnails 204 | ._* 205 | 206 | # Files that might appear on external disk 207 | .Spotlight-V100 208 | .Trashes 209 | 210 | # Directories potentially created on remote AFP share 211 | .AppleDB 212 | .AppleDesktop 213 | Network Trash Folder 214 | Temporary Items 215 | .apdisk 216 | 217 | # Windows 218 | # ========================= 219 | 220 | # Windows image file caches 221 | Thumbs.db 222 | ehthumbs.db 223 | 224 | # Folder config file 225 | Desktop.ini 226 | 227 | # Recycle Bin used on file shares 228 | $RECYCLE.BIN/ 229 | 230 | # Windows Installer files 231 | *.cab 232 | *.msi 233 | *.msm 234 | *.msp 235 | -------------------------------------------------------------------------------- /DemoU2FSite/Content/themes/base/datepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Datepicker 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/datepicker/#theming 10 | */ 11 | .ui-datepicker { 12 | width: 17em; 13 | padding: .2em .2em 0; 14 | display: none; 15 | } 16 | .ui-datepicker .ui-datepicker-header { 17 | position: relative; 18 | padding: .2em 0; 19 | } 20 | .ui-datepicker .ui-datepicker-prev, 21 | .ui-datepicker .ui-datepicker-next { 22 | position: absolute; 23 | top: 2px; 24 | width: 1.8em; 25 | height: 1.8em; 26 | } 27 | .ui-datepicker .ui-datepicker-prev-hover, 28 | .ui-datepicker .ui-datepicker-next-hover { 29 | top: 1px; 30 | } 31 | .ui-datepicker .ui-datepicker-prev { 32 | left: 2px; 33 | } 34 | .ui-datepicker .ui-datepicker-next { 35 | right: 2px; 36 | } 37 | .ui-datepicker .ui-datepicker-prev-hover { 38 | left: 1px; 39 | } 40 | .ui-datepicker .ui-datepicker-next-hover { 41 | right: 1px; 42 | } 43 | .ui-datepicker .ui-datepicker-prev span, 44 | .ui-datepicker .ui-datepicker-next span { 45 | display: block; 46 | position: absolute; 47 | left: 50%; 48 | margin-left: -8px; 49 | top: 50%; 50 | margin-top: -8px; 51 | } 52 | .ui-datepicker .ui-datepicker-title { 53 | margin: 0 2.3em; 54 | line-height: 1.8em; 55 | text-align: center; 56 | } 57 | .ui-datepicker .ui-datepicker-title select { 58 | font-size: 1em; 59 | margin: 1px 0; 60 | } 61 | .ui-datepicker select.ui-datepicker-month, 62 | .ui-datepicker select.ui-datepicker-year { 63 | width: 45%; 64 | } 65 | .ui-datepicker table { 66 | width: 100%; 67 | font-size: .9em; 68 | border-collapse: collapse; 69 | margin: 0 0 .4em; 70 | } 71 | .ui-datepicker th { 72 | padding: .7em .3em; 73 | text-align: center; 74 | font-weight: bold; 75 | border: 0; 76 | } 77 | .ui-datepicker td { 78 | border: 0; 79 | padding: 1px; 80 | } 81 | .ui-datepicker td span, 82 | .ui-datepicker td a { 83 | display: block; 84 | padding: .2em; 85 | text-align: right; 86 | text-decoration: none; 87 | } 88 | .ui-datepicker .ui-datepicker-buttonpane { 89 | background-image: none; 90 | margin: .7em 0 0 0; 91 | padding: 0 .2em; 92 | border-left: 0; 93 | border-right: 0; 94 | border-bottom: 0; 95 | } 96 | .ui-datepicker .ui-datepicker-buttonpane button { 97 | float: right; 98 | margin: .5em .2em .4em; 99 | cursor: pointer; 100 | padding: .2em .6em .3em .6em; 101 | width: auto; 102 | overflow: visible; 103 | } 104 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { 105 | float: left; 106 | } 107 | 108 | /* with multiple calendars */ 109 | .ui-datepicker.ui-datepicker-multi { 110 | width: auto; 111 | } 112 | .ui-datepicker-multi .ui-datepicker-group { 113 | float: left; 114 | } 115 | .ui-datepicker-multi .ui-datepicker-group table { 116 | width: 95%; 117 | margin: 0 auto .4em; 118 | } 119 | .ui-datepicker-multi-2 .ui-datepicker-group { 120 | width: 50%; 121 | } 122 | .ui-datepicker-multi-3 .ui-datepicker-group { 123 | width: 33.3%; 124 | } 125 | .ui-datepicker-multi-4 .ui-datepicker-group { 126 | width: 25%; 127 | } 128 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, 129 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { 130 | border-left-width: 0; 131 | } 132 | .ui-datepicker-multi .ui-datepicker-buttonpane { 133 | clear: left; 134 | } 135 | .ui-datepicker-row-break { 136 | clear: both; 137 | width: 100%; 138 | font-size: 0; 139 | } 140 | 141 | /* RTL support */ 142 | .ui-datepicker-rtl { 143 | direction: rtl; 144 | } 145 | .ui-datepicker-rtl .ui-datepicker-prev { 146 | right: 2px; 147 | left: auto; 148 | } 149 | .ui-datepicker-rtl .ui-datepicker-next { 150 | left: 2px; 151 | right: auto; 152 | } 153 | .ui-datepicker-rtl .ui-datepicker-prev:hover { 154 | right: 1px; 155 | left: auto; 156 | } 157 | .ui-datepicker-rtl .ui-datepicker-next:hover { 158 | left: 1px; 159 | right: auto; 160 | } 161 | .ui-datepicker-rtl .ui-datepicker-buttonpane { 162 | clear: right; 163 | } 164 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { 165 | float: left; 166 | } 167 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, 168 | .ui-datepicker-rtl .ui-datepicker-group { 169 | float: right; 170 | } 171 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, 172 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { 173 | border-right-width: 0; 174 | border-left-width: 1px; 175 | } 176 | -------------------------------------------------------------------------------- /u2flib/Data/Messages/ClientData.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | using Newtonsoft.Json.Linq; 16 | using u2flib.Exceptions; 17 | using u2flib.Util; 18 | 19 | namespace u2flib.Data.Messages 20 | { 21 | public class ClientData 22 | { 23 | private const String TypeParam = "typ"; 24 | private const String ChallengeParam = "challenge"; 25 | private const String OriginParam = "origin"; 26 | 27 | public String Type { get; private set; } 28 | public String Challenge { get; private set; } 29 | public String Origin { get; private set; } 30 | public String RawClientData { get; private set; } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The client data. 36 | /// ClientData has wrong format 37 | public ClientData(String clientData) 38 | { 39 | try 40 | { 41 | RawClientData = Utils.GetString(Utils.Base64StringToByteArray(clientData)); 42 | 43 | JObject element = JObject.Parse(RawClientData); 44 | if (element == null) 45 | throw new U2fException("ClientData has wrong format"); 46 | 47 | JToken theType, theChallenge, theOrgin; 48 | if (!element.TryGetValue(TypeParam, out theType)) 49 | throw new U2fException("Bad clientData: missing 'typ' param"); 50 | if (!element.TryGetValue(ChallengeParam, out theChallenge)) 51 | throw new U2fException("Bad clientData: missing 'challenge' param"); 52 | 53 | Type = theType.ToString(); 54 | Challenge = theChallenge.ToString(); 55 | if (element.TryGetValue(OriginParam, out theOrgin)) 56 | Origin = theOrgin.ToString(); 57 | } 58 | catch (Exception exception) 59 | { 60 | throw new U2fException("Invalid clientData format.: " + exception.Message); 61 | } 62 | } 63 | 64 | public void CheckContent(String type, String challenge, HashSet facets) 65 | { 66 | if (!type.Equals(Type)) 67 | { 68 | throw new U2fException("Bad clientData: bad type " + type); 69 | } 70 | if (!challenge.Equals(Challenge)) 71 | { 72 | throw new U2fException("Wrong challenge signed in clientData"); 73 | } 74 | if (facets != null) 75 | { 76 | VerifyOrigin(Origin, CanonicalizeOrigins(facets)); 77 | } 78 | } 79 | 80 | public String AsJson() 81 | { 82 | return RawClientData; 83 | } 84 | 85 | private void VerifyOrigin(String origin, HashSet allowedOrigins) 86 | { 87 | if (!allowedOrigins.Contains(CanonicalizeOrigin(origin))) 88 | { 89 | throw new UriFormatException(origin + 90 | " is not a recognized home origin for this backend"); 91 | } 92 | } 93 | 94 | private HashSet CanonicalizeOrigins(HashSet origins) 95 | { 96 | HashSet result = new HashSet(); 97 | foreach (string orgin in origins) 98 | { 99 | result.Add(CanonicalizeOrigin(orgin)); 100 | } 101 | return result; 102 | } 103 | 104 | private String CanonicalizeOrigin(String url) 105 | { 106 | try 107 | { 108 | Uri uri = new Uri(url); 109 | if (string.IsNullOrWhiteSpace(uri.Authority)) 110 | return url; 111 | 112 | return uri.Scheme + "://" + uri.Authority; 113 | } 114 | catch (UriFormatException e) 115 | { 116 | throw new UriFormatException("specified bad origin", e); 117 | } 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /u2flib/Data/Messages/StartedAuthentication.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Code was copied from java example so there is a .NET version 3 | * to work with. 4 | * 5 | * Copyright 2014 Yubico. 6 | * Copyright 2014 Google Inc. All rights reserved. 7 | * 8 | * Use of this source code is governed by a BSD-style 9 | * license that can be found in the LICENSE file or at 10 | * https://developers.google.com/open-source/licenses/bsd 11 | */ 12 | 13 | using System; 14 | using System.Linq; 15 | 16 | namespace u2flib.Data.Messages 17 | { 18 | public class StartedAuthentication : DataObject 19 | { 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The challenge. 24 | /// The application identifier. 25 | /// The key handle. 26 | public StartedAuthentication(String challenge, String appId, String keyHandle) 27 | { 28 | Version = U2F.U2FVersion; 29 | Challenge = challenge; 30 | AppId = appId; 31 | KeyHandle = keyHandle; 32 | } 33 | 34 | /// 35 | /// Gets the Version 36 | /// Version of the protocol that the to-be-registered U2F token must speak. For 37 | /// the version of the protocol described herein, must be "U2F_V2" 38 | /// 39 | /// 40 | /// The key handle. 41 | /// 42 | public String Version { get; private set; } 43 | 44 | /// 45 | /// Gets the key handle. 46 | /// websafe-base64 encoding of the key handle obtained from the U2F token 47 | /// during registration. 48 | /// 49 | /// 50 | /// The key handle. 51 | /// 52 | public String KeyHandle { get; private set; } 53 | 54 | /// 55 | /// Gets the challenge. 56 | /// The websafe-base64-encoded challenge. 57 | /// 58 | /// 59 | /// The challenge. 60 | /// 61 | public String Challenge { get; private set; } 62 | 63 | /// 64 | /// Gets the application identifier. 65 | /// The application id that the RP would like to assert. The U2F token will 66 | /// enforce that the key handle provided above is associated with this 67 | /// application id. The browser enforces that the calling origin belongs to the 68 | /// application identified by the application id. 69 | /// 70 | /// 71 | /// The application identifier. 72 | /// 73 | public String AppId { get; private set; } 74 | 75 | public override int GetHashCode() 76 | { 77 | int hash = 23 + Version.Sum(c => c + 31); 78 | hash += Challenge.Sum(c => c + 31); 79 | hash += AppId.Sum(c => c + 31); 80 | hash += KeyHandle.Sum(c => c + 31); 81 | 82 | return hash; 83 | } 84 | 85 | public override bool Equals(Object obj) 86 | { 87 | if (this == obj) 88 | return true; 89 | if (obj == null) 90 | return false; 91 | if (GetType() != obj.GetType()) 92 | return false; 93 | StartedAuthentication other = (StartedAuthentication) obj; 94 | if (AppId == null) 95 | { 96 | if (other.AppId != null) 97 | return false; 98 | } 99 | else if (!AppId.Equals(other.AppId)) 100 | return false; 101 | if (Challenge == null) 102 | { 103 | if (other.Challenge != null) 104 | return false; 105 | } 106 | else if (!Challenge.Equals(other.Challenge)) 107 | return false; 108 | if (KeyHandle == null) 109 | { 110 | if (other.KeyHandle != null) 111 | return false; 112 | } 113 | else if (!KeyHandle.Equals(other.KeyHandle)) 114 | return false; 115 | if (Version == null) 116 | { 117 | if (other.Version != null) 118 | return false; 119 | } 120 | else if (!Version.Equals(other.Version)) 121 | return false; 122 | return true; 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/FinishLogin.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @using Microsoft.Ajax.Utilities; 3 | @model DataModels.CompleteLoginModel 4 | 5 | @{ 6 | ViewBag.Title = "FinishAuthentication"; 7 | Layout = "~/Views/Shared/_Layout.cshtml"; 8 | } 9 | 10 |

Finish Authentication

11 | @using (Html.BeginForm("CompletedLogin", "Home", FormMethod.Post, new { id = "loginForm" })) 12 | { 13 | @Html.AntiForgeryToken() 14 | @Html.ValidationSummary() 15 | 16 |
17 | Registration Form 18 |

Please plug in your device and touch the button.

19 | 20 |
21 |
22 | @Html.LabelFor(m => m.UserName) 23 |
24 |
25 | @Html.TextBoxFor(m => m.UserName, new {@readonly = true}) 26 | @Html.ValidationMessageFor(m => m.UserName) 27 |
28 |
29 | 30 |
31 |
32 | @Html.LabelFor(m => m.AppId) 33 |
34 |
35 | 36 |
37 |
38 | 39 |
40 |
41 | @Html.LabelFor(m => m.Challenges) 42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 | @Html.LabelFor(m => m.Version) 51 |
52 |
53 | 54 |
55 |
56 | 57 | 58 |
59 |
60 | @Html.LabelFor(m => m.DeviceResponse) 61 | 62 |
63 |
64 | @Html.TextBoxFor(m => m.DeviceResponse) 65 |
66 |
67 |
68 | 69 |
70 | 71 | 72 | 88 | } 89 | 90 | @section Scripts { 91 | 119 | } -------------------------------------------------------------------------------- /DemoU2FSite/Views/Home/FinishRegister.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Mvc.Html 2 | @model DataModels.CompleteRegisterModel 3 | @{ 4 | ViewBag.Title = "Complete Register"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 | @section featured 9 | { 10 | 11 | @using (Html.BeginForm("CompleteRegister", "Home", FormMethod.Post, new {id = "registerForm"})) 12 | { 13 | @Html.AntiForgeryToken() 14 | @Html.ValidationSummary(true) 15 |
16 | Complete Registration Form 17 |

Please plug in your device and touch the button.

18 | 19 |
20 |
21 | @Html.LabelFor(m => m.UserName) 22 |
23 |
24 | @Html.TextBoxFor(m => m.UserName, new {@readonly = true}) 25 |
26 |
27 | 28 |
29 |
30 | @Html.LabelFor(m => m.AppId) 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | @Html.LabelFor(m => m.Version) 40 |
41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 | @Html.LabelFor(m => m.Challenge) 49 |
50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | @Html.LabelFor(m => m.DeviceResponse) 58 | 59 |
60 |
61 | @Html.TextBoxFor(m => m.DeviceResponse, new {@readonly = true}) 62 |
63 |
64 |
65 | 66 |
67 | } 68 | 69 | 70 | 86 | } 87 | 88 | @section Scripts { 89 | 90 | 119 | } 120 | --------------------------------------------------------------------------------