// 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 int 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 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 Optional<RetriesResponse> parse(const Vector<uint8_t>&); // retries is the number of PIN attempts remaining before the authenticator // locks. int 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 Optional<KeyAgreementResponse> parse(const Vector<uint8_t>&); WEBCORE_EXPORT static 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 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. 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 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)