| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Copyright (C) 2019 Apple Inc. All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * 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. |
| // * 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. |
| // |
| // 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. |
| |
| // This file contains structures to implement the CTAP2 PIN protocol, version |
| // one. See |
| // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#authenticatorClientPIN |
| |
| #pragma once |
| |
| #if ENABLE(WEB_AUTHN) |
| |
| #include "CBORValue.h" |
| #include "FidoConstants.h" |
| |
| namespace WebCore { |
| class CryptoKeyAES; |
| class CryptoKeyEC; |
| class CryptoKeyHMAC; |
| } |
| |
| namespace fido { |
| namespace pin { |
| |
| // Subcommand enumerates the subcommands to the main |authenticatorClientPIN| |
| // command. See |
| // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#authenticatorClientPIN |
| enum class Subcommand : uint8_t { |
| kGetRetries = 0x01, |
| kGetKeyAgreement = 0x02, |
| kSetPin = 0x03, |
| kChangePin = 0x04, |
| kGetPinToken = 0x05, |
| }; |
| |
| // RequestKey enumerates the keys in the top-level CBOR map for all PIN |
| // commands. |
| enum class RequestKey : uint8_t { |
| kProtocol = 1, |
| kSubcommand = 2, |
| kKeyAgreement = 3, |
| kPinAuth = 4, |
| kNewPinEnc = 5, |
| kPinHashEnc = 6, |
| }; |
| |
| // ResponseKey enumerates the keys in the top-level CBOR map for all PIN |
| // responses. |
| enum class ResponseKey : uint8_t { |
| kKeyAgreement = 1, |
| kPinToken = 2, |
| kRetries = 3, |
| }; |
| |
| // kProtocolVersion is the version of the PIN protocol that this code |
| // implements. |
| constexpr int64_t kProtocolVersion = 1; |
| |
| // encodeCOSEPublicKey takes a raw ECDH256 public key and returns it as a COSE structure. |
| WEBCORE_EXPORT cbor::CBORValue::MapValue encodeCOSEPublicKey(const Vector<uint8_t>& key); |
| |
| // encodeRawPublicKey takes X & Y and returns them as a 0x04 || X || Y byte array. |
| WEBCORE_EXPORT Vector<uint8_t> encodeRawPublicKey(const Vector<uint8_t>& X, const Vector<uint8_t>& Y); |
| |
| // validateAndConvertToUTF8 convert the input to a UTF8 CString if it is a syntactically valid PIN. |
| WEBCORE_EXPORT std::optional<CString> validateAndConvertToUTF8(const String& pin); |
| |
| // kMinBytes is the minimum number of *bytes* of PIN data that a CTAP2 device |
| // will accept. Since the PIN is UTF-8 encoded, this could be a single code |
| // point. However, the platform is supposed to additionally enforce a 4 |
| // *character* minimum |
| constexpr size_t kMinBytes = 4; |
| // kMaxBytes is the maximum number of bytes of PIN data that a CTAP2 device will |
| // accept. |
| constexpr size_t kMaxBytes = 63; |
| |
| // RetriesRequest asks an authenticator for the number of remaining PIN attempts |
| // before the device is locked. |
| struct RetriesRequest { |
| }; |
| |
| // RetriesResponse reflects an authenticator's response to a |RetriesRequest|. |
| struct RetriesResponse { |
| WEBCORE_EXPORT static std::optional<RetriesResponse> parse(const Vector<uint8_t>&); |
| |
| // retries is the number of PIN attempts remaining before the authenticator |
| // locks. |
| uint64_t retries; |
| |
| private: |
| RetriesResponse(); |
| }; |
| |
| // KeyAgreementRequest asks an authenticator for an ephemeral ECDH key for |
| // encrypting PIN material in future requests. |
| struct KeyAgreementRequest { |
| }; |
| |
| // KeyAgreementResponse reflects an authenticator's response to a |
| // |KeyAgreementRequest| and is also used as representation of the |
| // authenticator's ephemeral key. |
| struct KeyAgreementResponse { |
| WEBCORE_EXPORT static std::optional<KeyAgreementResponse> parse(const Vector<uint8_t>&); |
| WEBCORE_EXPORT static std::optional<KeyAgreementResponse> parseFromCOSE(const cbor::CBORValue::MapValue&); |
| |
| Ref<WebCore::CryptoKeyEC> peerKey; |
| |
| private: |
| explicit KeyAgreementResponse(Ref<WebCore::CryptoKeyEC>&&); |
| }; |
| |
| // TokenRequest requests a pin-token from an authenticator. These tokens can be |
| // used to show user-verification in other operations, e.g. when getting an |
| // assertion. |
| class TokenRequest { |
| WTF_MAKE_NONCOPYABLE(TokenRequest); |
| public: |
| WEBCORE_EXPORT static std::optional<TokenRequest> tryCreate(const CString& pin, const WebCore::CryptoKeyEC&); |
| TokenRequest(TokenRequest&&) = default; |
| |
| // sharedKey returns the shared ECDH key that was used to encrypt the PIN. |
| // This is needed to decrypt the response. |
| WEBCORE_EXPORT const WebCore::CryptoKeyAES& sharedKey() const; |
| |
| friend Vector<uint8_t> encodeAsCBOR(const TokenRequest&); |
| |
| private: |
| TokenRequest(Ref<WebCore::CryptoKeyAES>&& sharedKey, cbor::CBORValue::MapValue&& coseKey, Vector<uint8_t>&& pinHash); |
| |
| Ref<WebCore::CryptoKeyAES> m_sharedKey; |
| mutable cbor::CBORValue::MapValue m_coseKey; |
| Vector<uint8_t> m_pinHash; // Only the left 16 bytes are kept. |
| }; |
| |
| // TokenResponse represents the response to a pin-token request. In order to |
| // decrypt a response, the shared key from the request is needed. Once a pin- |
| // token has been decrypted, it can be used to calculate the pinAuth parameters |
| // needed to show user-verification in future operations. |
| class TokenResponse { |
| public: |
| WEBCORE_EXPORT static std::optional<TokenResponse> parse(const WebCore::CryptoKeyAES& sharedKey, const Vector<uint8_t>& inBuffer); |
| |
| // pinAuth returns a pinAuth parameter for a request that will use the given |
| // client-data hash. |
| WEBCORE_EXPORT Vector<uint8_t> pinAuth(const Vector<uint8_t>& clientDataHash) const; |
| |
| WEBCORE_EXPORT const Vector<uint8_t>& token() const; |
| |
| private: |
| explicit TokenResponse(Ref<WebCore::CryptoKeyHMAC>&&); |
| |
| Ref<WebCore::CryptoKeyHMAC> m_token; |
| }; |
| |
| WEBCORE_EXPORT Vector<uint8_t> encodeAsCBOR(const RetriesRequest&); |
| WEBCORE_EXPORT Vector<uint8_t> encodeAsCBOR(const KeyAgreementRequest&); |
| WEBCORE_EXPORT Vector<uint8_t> encodeAsCBOR(const TokenRequest&); |
| |
| } // namespace pin |
| } // namespace fido |
| |
| #endif // ENABLE(WEB_AUTHN) |