1 | // Copyright 2018 The Chromium Authors. All rights reserved. |
2 | // Copyright (C) 2019 Apple Inc. All rights reserved. |
3 | // |
4 | // Redistribution and use in source and binary forms, with or without |
5 | // modification, are permitted provided that the following conditions are |
6 | // met: |
7 | // |
8 | // * Redistributions of source code must retain the above copyright |
9 | // notice, this list of conditions and the following disclaimer. |
10 | // * Redistributions in binary form must reproduce the above |
11 | // copyright notice, this list of conditions and the following disclaimer |
12 | // in the documentation and/or other materials provided with the |
13 | // distribution. |
14 | // * Neither the name of Google Inc. nor the names of its |
15 | // contributors may be used to endorse or promote products derived from |
16 | // this software without specific prior written permission. |
17 | // |
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | |
30 | #include "config.h" |
31 | #include "U2fCommandConstructor.h" |
32 | |
33 | #if ENABLE(WEB_AUTHN) |
34 | |
35 | #include "ApduCommand.h" |
36 | #include "FidoConstants.h" |
37 | #include "PublicKeyCredentialCreationOptions.h" |
38 | #include "PublicKeyCredentialRequestOptions.h" |
39 | #include "UserVerificationRequirement.h" |
40 | #include "WebAuthenticationConstants.h" |
41 | #include "WebAuthenticationUtils.h" |
42 | #include <wtf/Optional.h> |
43 | |
44 | namespace fido { |
45 | using namespace WebCore; |
46 | |
47 | namespace { |
48 | |
49 | static Vector<uint8_t> constructU2fRegisterCommand(const Vector<uint8_t>& applicationParameter, const Vector<uint8_t>& challengeParameter) |
50 | { |
51 | Vector<uint8_t> data; |
52 | data.reserveInitialCapacity(kU2fChallengeParamLength + kU2fApplicationParamLength); |
53 | data.appendVector(challengeParameter); |
54 | data.appendVector(applicationParameter); |
55 | |
56 | apdu::ApduCommand command; |
57 | command.setIns(static_cast<uint8_t>(U2fApduInstruction::kRegister)); |
58 | // This is needed for test of user presence even though the spec doesn't specify it. |
59 | command.setP1(kP1EnforceUserPresenceAndSign); |
60 | command.setData(WTFMove(data)); |
61 | command.setResponseLength(apdu::ApduCommand::kApduMaxResponseLength); |
62 | return command.getEncodedCommand(); |
63 | } |
64 | |
65 | static Optional<Vector<uint8_t>> constructU2fSignCommand(const Vector<uint8_t>& applicationParameter, const Vector<uint8_t>& challengeParameter, const Vector<uint8_t>& keyHandle, bool checkOnly) |
66 | { |
67 | if (keyHandle.size() > kMaxKeyHandleLength) |
68 | return WTF::nullopt; |
69 | |
70 | Vector<uint8_t> data; |
71 | data.reserveInitialCapacity(kU2fChallengeParamLength + kU2fApplicationParamLength + 1 + keyHandle.size()); |
72 | data.appendVector(challengeParameter); |
73 | data.appendVector(applicationParameter); |
74 | data.append(static_cast<uint8_t>(keyHandle.size())); |
75 | data.appendVector(keyHandle); |
76 | |
77 | apdu::ApduCommand command; |
78 | command.setIns(static_cast<uint8_t>(U2fApduInstruction::kSign)); |
79 | command.setP1(checkOnly ? kP1CheckOnly : kP1EnforceUserPresenceAndSign); |
80 | command.setData(WTFMove(data)); |
81 | command.setResponseLength(apdu::ApduCommand::kApduMaxResponseLength); |
82 | return command.getEncodedCommand(); |
83 | } |
84 | |
85 | } // namespace |
86 | |
87 | bool isConvertibleToU2fRegisterCommand(const PublicKeyCredentialCreationOptions& request) |
88 | { |
89 | if (request.authenticatorSelection && (request.authenticatorSelection->userVerification == UserVerificationRequirement::Required || request.authenticatorSelection->requireResidentKey)) |
90 | return false; |
91 | if (request.pubKeyCredParams.findMatching([](auto& item) { return item.alg == COSE::ES256; }) == notFound) |
92 | return false; |
93 | return true; |
94 | } |
95 | |
96 | bool isConvertibleToU2fSignCommand(const PublicKeyCredentialRequestOptions& request) |
97 | { |
98 | return (request.userVerification != UserVerificationRequirement::Required) && !request.allowCredentials.isEmpty(); |
99 | } |
100 | |
101 | Optional<Vector<uint8_t>> convertToU2fRegisterCommand(const Vector<uint8_t>& clientDataHash, const PublicKeyCredentialCreationOptions& request) |
102 | { |
103 | if (!isConvertibleToU2fRegisterCommand(request)) |
104 | return WTF::nullopt; |
105 | |
106 | return constructU2fRegisterCommand(produceRpIdHash(request.rp.id), clientDataHash); |
107 | } |
108 | |
109 | Optional<Vector<uint8_t>> convertToU2fCheckOnlySignCommand(const Vector<uint8_t>& clientDataHash, const PublicKeyCredentialCreationOptions& request, const PublicKeyCredentialDescriptor& keyHandle) |
110 | { |
111 | if (keyHandle.type != PublicKeyCredentialType::PublicKey) |
112 | return WTF::nullopt; |
113 | |
114 | return constructU2fSignCommand(produceRpIdHash(request.rp.id), clientDataHash, keyHandle.idVector, true /* checkOnly */); |
115 | } |
116 | |
117 | Optional<Vector<uint8_t>> convertToU2fSignCommand(const Vector<uint8_t>& clientDataHash, const PublicKeyCredentialRequestOptions& request, const Vector<uint8_t>& keyHandle, bool isAppId) |
118 | { |
119 | if (!isConvertibleToU2fSignCommand(request)) |
120 | return WTF::nullopt; |
121 | |
122 | if (!isAppId) |
123 | return constructU2fSignCommand(produceRpIdHash(request.rpId), clientDataHash, keyHandle, false); |
124 | ASSERT(request.extensions && !request.extensions->appid.isNull()); |
125 | return constructU2fSignCommand(produceRpIdHash(request.extensions->appid), clientDataHash, keyHandle, false); |
126 | } |
127 | |
128 | Vector<uint8_t> constructBogusU2fRegistrationCommand() |
129 | { |
130 | return constructU2fRegisterCommand(convertBytesToVector(kBogusAppParam, sizeof(kBogusAppParam)), convertBytesToVector(kBogusChallenge, sizeof(kBogusChallenge))); |
131 | } |
132 | |
133 | } // namespace fido |
134 | |
135 | #endif // ENABLE(WEB_AUTHN) |
136 | |