Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
100% test coverage, cleanup, docs
  • Loading branch information
e3b0c442 committed Jan 25, 2020
commit 1f9f1870d279f92d4ac3ace8ce06e1a31482a8bb
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ 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.3.0] - 2020-01-21]
## [0.4.0] - 2020-01-25
### Added
- Attestation verification for the _packed_ format has been added, continuing the previous guidance for trust chain validation. ECDAA attestation type is not supported due to lack of ECDAA support in Go standard library.

## [0.3.0] - 2020-01-21
### Added
- Attestation verification for the _fido-u2f_ format has been added. The attestation type and trust path are __not__ validated, only the signature against the provided certificate. It is up to the implementor to verify the trust chain using the `*AttestationObject` returned from `FinishRegistration`.
### Changed
Expand All @@ -18,7 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- `UnmarshalBinary` and `MarshalBinary` methods on `AttestationObject` and `AuthenticatorData`, implementing the `BinaryMarshaler` and `BinaryUnmarshaler` interfaces
- `Encode` method on `AuthenticatorData` to facilitate encoding `AttestationObject` for storage

### Changed
- **[BREAKING]** `FinishRegistration` now returns `(*AttestationObject, error)` instead of `(string, []byte, error)`, to allow the implementor to choose how much or little of the authenticator data to save.
- **[BREAKING]** `FinishAuthentication` now returns `(*AuthenticatorData, error)` instead of `(uint, error)`, to allow the implementor full access to the authenticator data for other uses
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ _warp_ was built with the following goals in mind:
* Supported: ES256, ES384, ES512, EdDSA, RS1, RS256, RS384, RS512, PS256, PS384, PS512
* To be implemented: None plannned
* Attestation formats
* Supported: _fido-u2f_, _none_
* To be implemented: _packed_, _tpm_, _android-key_, _android-safetynet_
* 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_
Expand Down
27 changes: 3 additions & 24 deletions attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ func TestVerifyFIDOU2FAttestationStatement(t *testing.T) {
0xfb, 0xab, 0xbb, 0xb8, 0x5, 0x5a, 0xd, 0x98,
0x63, // text string 3 chars
0x78, 0x35, 0x63, // "x5c"
0x81, // array, 1 member
0x58, 0xf6, // byte string, 246 bytes
0x81, // array, 1 member
0x59, 0x01, 0xab, // byte string, 427 bytes
}, goodP256CertBytes...),
RawAuthData: []byte{
0xd8, 0x33, 0x51, 0x40, 0x80, 0xa0, 0xc7, 0x2b, //authdata.rpIDHash
Expand Down Expand Up @@ -1203,28 +1203,7 @@ func TestVerifyPackedAttestationStatement(t *testing.T) {
Name: "Good X5C",
RawAuthData: mockRawAuthData,
ClientDataHash: mockCreateClientDataJSONHash,
AttStmt: append([]byte{
0xa3, // map 2 items
0x63, // text string 3 chars
0x61, 0x6c, 0x67, // "alg"
0x26, // bogus alg
0x63, // text string 3 chars
0x73, 0x69, 0x67, // "sig"
0x58, 0x48, // byte string, 72 bytes
0x30, 0x46, 0x2, 0x21, 0x0, 0x96, 0x34, 0xfd,
0xfe, 0x8c, 0x20, 0xb2, 0x46, 0xb7, 0x67, 0x21,
0x67, 0xaa, 0x8a, 0x4c, 0xcd, 0x76, 0xcc, 0x84,
0xf3, 0x42, 0x7f, 0xa4, 0x2e, 0x5e, 0x1e, 0x41,
0x57, 0xc4, 0xd4, 0xfd, 0x31, 0x2, 0x21, 0x0,
0xc2, 0x5e, 0xa0, 0xf, 0x21, 0x31, 0x33, 0xd3,
0x1e, 0xbf, 0x34, 0x5e, 0x63, 0x6a, 0x9d, 0xcc,
0x6f, 0xa, 0xfc, 0xd0, 0x94, 0x12, 0x8b, 0x85,
0xd8, 0x7f, 0x8f, 0x5c, 0x9b, 0x49, 0xc9, 0x4a,
0x63, // text string 3 chars
0x78, 0x35, 0x63, // "x5c"
0x81, // array, 1 member
0x59, 0x01, 0xab, // byte string, 427 bytes
}, goodP256CertBytes...),
AttStmt: goodPackedAttStmt,
},
{
Name: "is ECDAA",
Expand Down
19 changes: 19 additions & 0 deletions registration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,25 @@ func TestFinishRegistration(t *testing.T) {
},
},
},
{
Name: "good packed attestation",
RP: mockRP,
CredFinder: errorCredFinder,
Opts: mockPublicKeyCredentialCreationOptions,
Cred: &AttestationPublicKeyCredential{
PublicKeyCredential: PublicKeyCredential{
CMCredential: CMCredential{
ID: "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU",
},
},
Response: AuthenticatorAttestationResponse{
AuthenticatorResponse: AuthenticatorResponse{
ClientDataJSON: mockCreateClientDataJSON,
},
AttestationObject: mockRawPackedAttestationObject,
},
},
},
}

for _, test := range tests {
Expand Down
103 changes: 82 additions & 21 deletions warp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ var mockRawAuthData []byte
var mockAuthData AuthenticatorData
var mockRawNoneAttestationObject cbor.RawMessage
var mockRawFIDOU2FAttestationObject cbor.RawMessage
var mockRawPackedAttestationObject cbor.RawMessage
var mockNoneAttestationObject AttestationObject
var goodPackedAttStmt []byte
var goodFIDOU2FAttStmt []byte
var mockCreateClientDataJSON []byte
var mockCreateClientDataJSONHash [32]byte
Expand Down Expand Up @@ -158,10 +160,6 @@ func TestMain(m *testing.M) {
log.Fatal(err)
}

//template.Subject.OrganizationalUnit = nil
//v2cert, err := x509.CreateCertificate(&predictableReader{}, &template, &template, &goodP256Key.PublicKey, goodP256Key)
//log.Fatalf("len %d: %#v", len(v2cert), v2cert)

goodP256Cert, err = x509.ParseCertificate(goodP256CertBytes)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -314,32 +312,43 @@ func TestMain(m *testing.M) {
0x59, 0x01, 0xab, // byte string, 427 bytes
}, goodP256CertBytes...)

/*verif := append(mockRawAuthData, mockCreateClientDataJSONHash[:]...)
vsum := sha256.Sum256(verif)
r, s, err := ecdsa.Sign(&predictableReader{}, goodP256Key, vsum[:])
if err != nil {
log.Fatal(err)
}

sigBytes, err := asn1.Marshal(struct{ R, S *big.Int }{R: r, S: s})
if err != nil {
log.Fatal(err)
}
goodPackedAttStmt = cbor.RawMessage(
append([]byte{
0xa3, // map 2 items
0x63, // text string 3 chars
0x61, 0x6c, 0x67, // "alg"
0x26, // bogus alg
0x63, // text string 3 chars
0x73, 0x69, 0x67, // "sig"
0x58, 0x48, // byte string, 72 bytes
0x30, 0x46, 0x2, 0x21, 0x0, 0x96, 0x34, 0xfd,
0xfe, 0x8c, 0x20, 0xb2, 0x46, 0xb7, 0x67, 0x21,
0x67, 0xaa, 0x8a, 0x4c, 0xcd, 0x76, 0xcc, 0x84,
0xf3, 0x42, 0x7f, 0xa4, 0x2e, 0x5e, 0x1e, 0x41,
0x57, 0xc4, 0xd4, 0xfd, 0x31, 0x2, 0x21, 0x0,
0xc2, 0x5e, 0xa0, 0xf, 0x21, 0x31, 0x33, 0xd3,
0x1e, 0xbf, 0x34, 0x5e, 0x63, 0x6a, 0x9d, 0xcc,
0x6f, 0xa, 0xfc, 0xd0, 0x94, 0x12, 0x8b, 0x85,
0xd8, 0x7f, 0x8f, 0x5c, 0x9b, 0x49, 0xc9, 0x4a,
0x63, // text string 3 chars
0x78, 0x35, 0x63, // "x5c"
0x81, // array, 1 member
0x59, 0x01, 0xab, // byte string, 427 bytes
}, goodP256CertBytes...),
)

log.Fatalf("len %d: %#v", len(sigBytes), sigBytes)
*/
mockRawFIDOU2FAttestationObject = cbor.RawMessage(
mockRawPackedAttestationObject = cbor.RawMessage(
append(
append(
[]byte{
0xa3, // map, 3 items
0x63, // text string, 3 chars
0x66, 0x6d, 0x74, // "fmt"
0x68, // text string, 8 chars
0x66, 0x69, 0x64, 0x6f, 0x2d, 0x75, 0x32, 0x66, // "fido-u2f"
0x66, // text string, 6 chars
0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, // "packed"
0x67, // text string, 7 chars
0x61, 0x74, 0x74, 0x53, 0x74, 0x6d, 0x74, // "attStmt"
}, goodFIDOU2FAttStmt...),
}, goodPackedAttStmt...),
[]byte{
0x68, // text string, 8 chars
0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, // "authData"
Expand Down Expand Up @@ -382,6 +391,58 @@ func TestMain(m *testing.M) {
),
)

mockRawFIDOU2FAttestationObject = append(
append(
[]byte{
0xa3, // map, 3 items
0x63, // text string, 3 chars
0x66, 0x6d, 0x74, // "fmt"
0x68, // text string, 8 chars
0x66, 0x69, 0x64, 0x6f, 0x2d, 0x75, 0x32, 0x66, // "fido-u2f"
0x67, // text string, 7 chars
0x61, 0x74, 0x74, 0x53, 0x74, 0x6d, 0x74, // "attStmt"
}, goodFIDOU2FAttStmt...),
[]byte{
0x68, // text string, 8 chars
0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, // "authData"
0x58, 0xa4, // byte string, 164 chars
0xd8, 0x33, 0x51, 0x40, 0x80, 0xa0, 0xc7, 0x2b, //authdata.rpIDHash
0x1e, 0xfa, 0x42, 0xb1, 0x8c, 0x96, 0xb9, 0x27, // |
0x3e, 0x9f, 0x19, 0x3f, 0xa9, 0x80, 0xdb, 0x09, // |
0xa0, 0x93, 0x33, 0x86, 0x5c, 0x2b, 0x32, 0xf3, // v
0x41, // authData.Flags
0x00, 0x00, 0x00, 0x01, // authData.SignCount
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // authData.attestedCredentialData.aaguid
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // v
0x00, 0x20, // authData.attestedCredentialData.credentialIDLength = 32
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, // authData.attestedCredentialData.credentialID
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, // |
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, // |
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, // v
0xa5, // map of 5 items
0x1, // key 1 (Kty)
0x2, // 2 (EC2 key)
0x3, // key 3 (Alg)
0x26, // -7
0x20, // key -1
0x1, // 1 (P256 Curve)
0x21, // key -2
0x58, // byte string, >24 bytes
0x20, // 32 bytes length
0x16, 0x16, 0xd7, 0xd0, 0x6a, 0x17, 0xd4, 0xff,
0xbf, 0x16, 0x69, 0x3e, 0x6c, 0x60, 0x5, 0xe6,
0xc7, 0x9, 0x16, 0x71, 0x6a, 0xf1, 0x3e, 0x95,
0xc2, 0xf2, 0xda, 0xc8, 0x6, 0x7, 0x2e, 0x8d,
0x22, // key -3
0x58, // byte string, >24 bytes
0x20, // 32 bytes length
0xb6, 0x72, 0x4, 0x62, 0x42, 0x44, 0x45, 0x2b,
0x96, 0x4f, 0x5c, 0xab, 0x16, 0x1c, 0xd3, 0xc,
0x76, 0x72, 0x6b, 0x9b, 0x36, 0x1d, 0xca, 0xdc,
0xda, 0x2, 0xef, 0x1a, 0x5c, 0x71, 0xac, 0x78,
}...,
)

mockNoneAttestationObject = AttestationObject{
AuthData: mockAuthData,
Fmt: AttestationFormatNone,
Expand Down