[BACK]Return to PROTOCOL.u2f CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/PROTOCOL.u2f, Revision 1.7

1.1       djm         1: This document describes OpenSSH's support for U2F/FIDO security keys.
                      2:
                      3: Background
                      4: ----------
                      5:
                      6: U2F is an open standard for two-factor authentication hardware, widely
                      7: used for user authentication to websites. U2F tokens are ubiquitous,
                      8: available from a number of manufacturers and are currently by far the
                      9: cheapest way for users to achieve hardware-backed credential storage.
                     10:
                     11: The U2F protocol however cannot be trivially used as an SSH protocol key
                     12: type as both the inputs to the signature operation and the resultant
                     13: signature differ from those specified for SSH. For similar reasons,
                     14: integration of U2F devices cannot be achieved via the PKCS#11 API.
                     15:
                     16: U2F also offers a number of features that are attractive in the context
                     17: of SSH authentication. They can be configured to require indication
                     18: of "user presence" for each signature operation (typically achieved
                     19: by requiring the user touch the key). They also offer an attestation
                     20: mechanism at key enrollment time that can be used to prove that a
                     21: given key is backed by hardware. Finally the signature format includes
                     22: a monotonic signature counter that can be used (at scale) to detect
                     23: concurrent use of a private key, should it be extracted from hardware.
                     24:
1.2       naddy      25: U2F private keys are generated through an enrollment operation,
1.1       djm        26: which takes an application ID - a URL-like string, typically "ssh:"
                     27: in this case, but a HTTP origin for the case of web authentication,
                     28: and a challenge string (typically randomly generated). The enrollment
                     29: operation returns a public key, a key handle that must be used to invoke
                     30: the hardware-backed private key, some flags and signed attestation
1.2       naddy      31: information that may be used to verify that a private key is hosted on a
1.1       djm        32: particular hardware instance.
                     33:
                     34: It is common for U2F hardware to derive private keys from the key handle
                     35: in conjunction with a small per-device secret that is unique to the
                     36: hardware, thus requiring little on-device storage for an effectively
                     37: unlimited number of supported keys. This drives the requirement that
                     38: the key handle be supplied for each signature operation. U2F tokens
1.7     ! djm        39: primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2
        !            40: standard specified additional key types include one based on Ed25519.
1.1       djm        41:
                     42: SSH U2F Key formats
                     43: -------------------
                     44:
1.7     ! djm        45: OpenSSH integrates U2F as new key and corresponding certificate types:
1.1       djm        46:
                     47:        sk-ecdsa-sha2-nistp256@openssh.com
                     48:        sk-ecdsa-sha2-nistp256-cert-v01@openssh.com
1.7     ! djm        49:        sk-ssh-ed25519@openssh.com
        !            50:        sk-ssh-ed25519-cert-v01@openssh.com
1.1       djm        51:
                     52: These key types are supported only for user authentication with the
                     53: "publickey" method. They are not used for host-based user authentication
                     54: or server host key authentication.
                     55:
                     56: While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
                     57: keys require extra information in the public and private keys, and in
                     58: the signature object itself. As such they cannot be made compatible with
                     59: the existing ecdsa-sha2-nistp* key types.
                     60:
                     61: The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
                     62:
                     63:        string          "sk-ecdsa-sha2-nistp256@openssh.com"
1.5       djm        64:        string          curve name
1.1       djm        65:        ec_point        Q
                     66:        string          application (user-specified, but typically "ssh:")
                     67:
                     68: The corresponding private key contains:
                     69:
                     70:        string          "sk-ecdsa-sha2-nistp256@openssh.com"
1.5       djm        71:        string          curve name
1.1       djm        72:        ec_point        Q
                     73:        string          application (user-specified, but typically "ssh:")
1.6       djm        74:        uint8           flags
1.1       djm        75:        string          key_handle
                     76:        string          reserved
                     77:
1.7     ! djm        78: The format of a sk-ssh-ed25519@openssh.com public key is:
        !            79:
        !            80:        string          "sk-ssh-ed25519@openssh.com"
        !            81:        string          public key
        !            82:        string          application (user-specified, but typically "ssh:")
        !            83:
        !            84: With a private half consisting of:
        !            85:
        !            86:        string          "sk-ssh-ed25519@openssh.com"
        !            87:        string          public key
        !            88:        string          application (user-specified, but typically "ssh:")
        !            89:        uint32          flags
        !            90:        string          key_handle
        !            91:        string          reserved
        !            92:
        !            93: The certificate form for SSH U2F keys appends the usual certificate
1.1       djm        94: information to the public key:
                     95:
1.2       naddy      96:        string          "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
1.1       djm        97:        string          nonce
1.5       djm        98:        string          curve name
1.1       djm        99:        ec_point        Q
                    100:        string          application
                    101:        uint64          serial
                    102:        uint32          type
                    103:        string          key id
                    104:        string          valid principals
                    105:        uint64          valid after
                    106:        uint64          valid before
                    107:        string          critical options
                    108:        string          extensions
                    109:        string          reserved
                    110:        string          signature key
                    111:        string          signature
                    112:
1.7     ! djm       113:        string          "sk-ssh-ed25519-cert-v01@openssh.com"
        !           114:        string          nonce
        !           115:        string          public key
        !           116:        string          application
        !           117:        uint64          serial
        !           118:        uint32          type
        !           119:        string          key id
        !           120:        string          valid principals
        !           121:        uint64          valid after
        !           122:        uint64          valid before
        !           123:        string          critical options
        !           124:        string          extensions
        !           125:        string          reserved
        !           126:        string          signature key
        !           127:        string          signature
        !           128:
1.1       djm       129: During key generation, the hardware also returns attestation information
                    130: that may be used to cryptographically prove that a given key is
                    131: hardware-backed. Unfortunately, the protocol required for this proof is
                    132: not privacy-preserving and may be used to identify U2F tokens with at
                    133: least manufacturer and batch number granularity. For this reason, we
                    134: choose not to include this information in the public key or save it by
                    135: default.
                    136:
                    137: Attestation information is very useful however in an organisational
1.2       naddy     138: context, where it may be used by a CA as part of certificate
1.1       djm       139: issuance. In this case, exposure to the CA of hardware identity is
                    140: desirable. To support this case, OpenSSH optionally allows retaining the
                    141: attestation information at the time of key generation. It will take the
                    142: following format:
                    143:
                    144:        string          "sk-attest-v00"
                    145:        uint32          version         (1 for U2F, 2 for FIDO2 in future)
                    146:        string          attestation certificate
                    147:        string          enrollment signature
                    148:
                    149: SSH U2F signatures
                    150: ------------------
                    151:
                    152: In addition to the message to be signed, the U2F signature operation
                    153: requires a few additional parameters:
                    154:
                    155:        byte            control bits (e.g. "user presence required" flag)
                    156:        byte[32]        SHA256(message)
                    157:        byte[32]        SHA256(application)
                    158:        byte            key_handle length
                    159:        byte[]          key_handle
                    160:
                    161: This signature is signed over a blob that consists of:
                    162:
                    163:        byte[32]        SHA256(application)
                    164:        byte            flags (including "user present", extensions present)
                    165:        uint32          counter
                    166:        byte[]          extensions
                    167:        byte[32]        SHA256(message)
                    168:
                    169: The signature returned from U2F hardware takes the following format:
                    170:
                    171:        byte            flags (including "user present")
                    172:        uint32          counter
                    173:        byte[32]        ecdsa_signature (in X9.62 format).
                    174:
                    175: For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
                    176: format data in the pre-authentication attack surface. Therefore, the
                    177: signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
1.3       markus    178: be reformatted slightly and the ecdsa_signature_blob value has the encoding:
1.1       djm       179:
                    180:        mpint           r
                    181:        mpint           s
                    182:        byte            flags
                    183:        uint32          counter
                    184:
                    185: Where 'r' and 's' are extracted by the client or token middleware from the
                    186: ecdsa_signature field returned from the hardware.
                    187:
1.4       markus    188: For Ed25519 keys the signature is encoded as:
                    189:
                    190:        string          "sk-ssh-ed25519@openssh.com"
                    191:        string          signature
                    192:        byte            flags
                    193:        uint32          counter
                    194:
                    195:
1.1       djm       196: ssh-agent protocol extensions
                    197: -----------------------------
                    198:
1.2       naddy     199: ssh-agent requires a protocol extension to support U2F keys. At
1.1       djm       200: present the closest analogue to Security Keys in ssh-agent are PKCS#11
                    201: tokens, insofar as they require a middleware library to communicate with
                    202: the device that holds the keys. Unfortunately, the protocol message used
                    203: to add PKCS#11 keys to ssh-agent does not include any way to send the
                    204: key handle to the agent as U2F keys require.
                    205:
1.2       naddy     206: To avoid this, without having to add wholly new messages to the agent
                    207: protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
                    208: with a new key constraint extension to encode a path to the middleware
1.1       djm       209: library for the key. The format of this constraint extension would be:
                    210:
                    211:        byte            SSH_AGENT_CONSTRAIN_EXTENSION
                    212:        string          sk@openssh.com
                    213:        string          middleware path
                    214:
                    215: This constraint-based approach does not present any compatibility
                    216: problems.
                    217:
                    218: OpenSSH integration
                    219: -------------------
                    220:
                    221: U2F tokens may be attached via a number of means, including USB and NFC.
                    222: The USB interface is standardised around a HID protocol, but we want to
                    223: be able to support other transports as well as dummy implementations for
1.7     ! djm       224: regress testing. For this reason, OpenSSH shall support a dynamically-
        !           225: loaded middleware libraries to communicate with security keys, but offer
        !           226: support for the common case of USB HID security keys internally.
1.1       djm       227:
                    228: The middleware library need only expose a handful of functions:
                    229:
                    230:        /* Flags */
                    231:        #define SSH_SK_USER_PRESENCE_REQD       0x01
                    232:
1.3       markus    233:        /* Algs */
                    234:        #define SSH_SK_ECDSA                   0x00
                    235:        #define SSH_SK_ED25519                 0x01
                    236:
1.1       djm       237:        struct sk_enroll_response {
                    238:                uint8_t *public_key;
                    239:                size_t public_key_len;
                    240:                uint8_t *key_handle;
                    241:                size_t key_handle_len;
                    242:                uint8_t *signature;
                    243:                size_t signature_len;
                    244:                uint8_t *attestation_cert;
                    245:                size_t attestation_cert_len;
                    246:        };
                    247:
                    248:        struct sk_sign_response {
                    249:                uint8_t flags;
                    250:                uint32_t counter;
                    251:                uint8_t *sig_r;
                    252:                size_t sig_r_len;
                    253:                uint8_t *sig_s;
                    254:                size_t sig_s_len;
                    255:        };
                    256:
                    257:        /* Return the version of the middleware API */
                    258:        uint32_t sk_api_version(void);
                    259:
                    260:        /* Enroll a U2F key (private key generation) */
1.3       markus    261:        int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
1.1       djm       262:            const char *application, uint8_t flags,
                    263:            struct sk_enroll_response **enroll_response);
                    264:
                    265:        /* Sign a challenge */
1.3       markus    266:        int sk_sign(int alg, const uint8_t *message, size_t message_len,
1.1       djm       267:            const char *application,
                    268:            const uint8_t *key_handle, size_t key_handle_len,
                    269:            uint8_t flags, struct sk_sign_response **sign_response);
                    270:
                    271: In OpenSSH, these will be invoked by generalising the existing
                    272: ssh-pkcs11-helper mechanism to provide containment of the middleware from
                    273: ssh-agent.
                    274: