Skip to content

Commit

Permalink
Add implementations for txAuthSimple and txAuthGeneric extensions (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
e3b0c442 authored Feb 2, 2020
1 parent 7f45780 commit 868b6d3
Show file tree
Hide file tree
Showing 6 changed files with 664 additions and 6 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ language: go

go:
- 1.13.x
- 1.14beta1

before_install:
- go get -t -v ./...
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.0] - 2020-02-02
### Added
- Extension implementations for `txAuthSimple` and `txAuthGeneric`.

## [0.5.0] - 2020-01-26
### Added
- `RegistrationValidator` and `AuthenticationValidator` function type for passing additional validations into the `Finish...` functions. These functions take pointerl to the ceremonies' respective options and credential structs as arguments, and as such can modify those values as needed. If the functions return an error, the ceremony ends in error. The `Finish...` functions will continue to have all validations required by the specification implemented.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ _warp_ was built with the following goals in mind:
* Supported: _packed, _fido-u2f_, _none_
* To be implemented: _tpm_, _android-key_, _android-safetynet_
* Defined extensions
* Supported: _appid_
* To be implemented: _txAuthSimple_, _txAuthGeneric_, _authnSel_, _exts_, _uvi_, _loc_, _uvm_, _biometricPerfBounds_
* Supported: _appid_, _txAuthSimple_, _txAuthGeneric_
* To be implemented: _authnSel_, _exts_, _uvi_, _loc_, _uvm_, _biometricPerfBounds_

## High level API
WebAuthn relying parties have two responsibilities: managing the _registration ceremony_, and managing the _authentication ceremony_. In order to support these ceremonies, interfaces are defined such that the methods will return the required data.
Expand Down
117 changes: 114 additions & 3 deletions extensions.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package warp

import (
"bytes"
"crypto"
"encoding/base64"
"hash"
"strings"
)

//Identifiers for defined extensions
const (
ExtensionAppID = "appid"
Expand Down Expand Up @@ -36,24 +44,42 @@ func BuildExtensions(exts ...Extension) AuthenticationExtensionsClientInputs {
return extensions
}

//UseAppID adds the appid extension to the extensions object. §10.1
//UseAppID adds the appid extension to the extensions object
func UseAppID(appID string) Extension {
return func(e AuthenticationExtensionsClientInputs) {
e[ExtensionAppID] = appID
}
}

//UseTxAuthSimple adds the txAuthSimple extension to the extensions object
func UseTxAuthSimple(txAuthSimple string) Extension {
return func(e AuthenticationExtensionsClientInputs) {
e[ExtensionTxAuthSimple] = txAuthSimple
}
}

//UseTxAuthGeneric adds the txAuthGeneric extension to the extensions object
func UseTxAuthGeneric(contentType string, content []byte) Extension {
return func(e AuthenticationExtensionsClientInputs) {
e[ExtensionTxAuthGeneric] = map[string]interface{}{
"contentType": contentType,
"content": content,
}
}
}

//RegistrationExtensionValidators is a map to all extension validators for
//extensions allowed during the registration ceremony
var RegistrationExtensionValidators map[string]RegistrationValidator = map[string]RegistrationValidator{}

//AuthenticationExtensionValidators is a map to all extension validators for
//extensions allowed during the authentication ceremony
var AuthenticationExtensionValidators map[string]AuthenticationValidator = map[string]AuthenticationValidator{
ExtensionAppID: ValidateAppID(),
ExtensionAppID: ValidateAppID(),
ExtensionTxAuthSimple: ValidateTxAuthSimple(),
}

//ValidateAppID validates the AppID extension and updates the credential
//ValidateAppID validates the appid extension and updates the credential
//request options with the valid AppID as needed
func ValidateAppID() AuthenticationValidator {
return func(opts *PublicKeyCredentialRequestOptions, cred *AssertionPublicKeyCredential) error {
Expand Down Expand Up @@ -81,3 +107,88 @@ func ValidateAppID() AuthenticationValidator {
return nil
}
}

//ValidateTxAuthSimple validates the txAuthSimple extension
func ValidateTxAuthSimple() AuthenticationValidator {
return func(opts *PublicKeyCredentialRequestOptions, cred *AssertionPublicKeyCredential) error {
o, ok := cred.Extensions[ExtensionTxAuthSimple]
if !ok {
return nil // do not fail on client ignored extension
}
i, ok := opts.Extensions[ExtensionTxAuthSimple]
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthSimple extension present in credential but not requested in options"))
}
out, ok := o.(string)
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("unexpected type on txAuthSimple extension output"))
}
in, ok := i.(string)
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("unexpected type on txAuthSimple extension input"))
}
if strings.ReplaceAll(in, "\n", "") != strings.ReplaceAll(out, "\n", "") {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthSimple output differs from input"))
}
return nil
}
}

//ValidateTxAuthGeneric validates the txAuthGeneric extension
func ValidateTxAuthGeneric() AuthenticationValidator {
return func(opts *PublicKeyCredentialRequestOptions, cred *AssertionPublicKeyCredential) error {
o, ok := cred.Extensions[ExtensionTxAuthGeneric]
if !ok {
return nil // do not fail on client ignored extension
}
i, ok := opts.Extensions[ExtensionTxAuthGeneric]
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthSimple extension present in credential but not requested in options"))
}
outB64, ok := o.(string)
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("unexpected type on txAuthGeneric extension output"))
}
out, err := base64.StdEncoding.DecodeString(outB64)
if err != nil {
return ErrVerifyClientExtensionOutput.Wrap(NewError("unable to decode txAuthGeneric extension output"))
}
in, ok := i.(map[string]interface{})
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("unexpected type on txAuthGeneric extension input"))
}
if _, ok := in["contentType"]; !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric input missing contentType member"))
}
if _, ok := in["contentType"].(string); !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric input contentType invalid type"))
}
if _, ok := in["content"]; !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric input missing content member"))
}
inBytes, ok := in["content"].([]byte)
if !ok {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric input content invalid type"))
}

var hasher hash.Hash
switch len(out) {
case crypto.SHA1.Size():
hasher = crypto.SHA1.New()
case crypto.SHA256.Size():
hasher = crypto.SHA256.New()
case crypto.SHA384.Size():
hasher = crypto.SHA384.New()
case crypto.SHA512.Size():
hasher = crypto.SHA512.New()
default:
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric output digest unknown length"))
}
hasher.Write(inBytes)
if !bytes.Equal(hasher.Sum(nil), out) {
return ErrVerifyClientExtensionOutput.Wrap(NewError("txAuthGeneric returned hash does not match input"))
}

return nil
}
}
Loading

0 comments on commit 868b6d3

Please sign in to comment.