[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.21

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
1.14      naddy      40: standard specifies additional key types, including 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: While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
                     53: keys require extra information in the public and private keys, and in
                     54: the signature object itself. As such they cannot be made compatible with
                     55: the existing ecdsa-sha2-nistp* key types.
                     56:
                     57: The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
                     58:
                     59:        string          "sk-ecdsa-sha2-nistp256@openssh.com"
1.5       djm        60:        string          curve name
1.1       djm        61:        ec_point        Q
                     62:        string          application (user-specified, but typically "ssh:")
                     63:
                     64: The corresponding private key contains:
                     65:
                     66:        string          "sk-ecdsa-sha2-nistp256@openssh.com"
1.5       djm        67:        string          curve name
1.1       djm        68:        ec_point        Q
                     69:        string          application (user-specified, but typically "ssh:")
1.6       djm        70:        uint8           flags
1.1       djm        71:        string          key_handle
                     72:        string          reserved
                     73:
1.7       djm        74: The format of a sk-ssh-ed25519@openssh.com public key is:
                     75:
                     76:        string          "sk-ssh-ed25519@openssh.com"
                     77:        string          public key
                     78:        string          application (user-specified, but typically "ssh:")
                     79:
                     80: With a private half consisting of:
                     81:
                     82:        string          "sk-ssh-ed25519@openssh.com"
                     83:        string          public key
                     84:        string          application (user-specified, but typically "ssh:")
1.12      djm        85:        uint8           flags
1.7       djm        86:        string          key_handle
                     87:        string          reserved
                     88:
                     89: The certificate form for SSH U2F keys appends the usual certificate
1.1       djm        90: information to the public key:
                     91:
1.2       naddy      92:        string          "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
1.1       djm        93:        string          nonce
1.5       djm        94:        string          curve name
1.1       djm        95:        ec_point        Q
                     96:        string          application
                     97:        uint64          serial
                     98:        uint32          type
                     99:        string          key id
                    100:        string          valid principals
                    101:        uint64          valid after
                    102:        uint64          valid before
                    103:        string          critical options
                    104:        string          extensions
                    105:        string          reserved
                    106:        string          signature key
                    107:        string          signature
                    108:
1.12      djm       109: and for security key ed25519 certificates:
                    110:
1.7       djm       111:        string          "sk-ssh-ed25519-cert-v01@openssh.com"
                    112:        string          nonce
                    113:        string          public key
                    114:        string          application
                    115:        uint64          serial
                    116:        uint32          type
                    117:        string          key id
                    118:        string          valid principals
                    119:        uint64          valid after
                    120:        uint64          valid before
                    121:        string          critical options
                    122:        string          extensions
                    123:        string          reserved
                    124:        string          signature key
                    125:        string          signature
                    126:
1.12      djm       127: Both security key certificates use the following encoding for private keys:
                    128:
                    129:        string          type (e.g. "sk-ssh-ed25519-cert-v01@openssh.com")
                    130:        string          pubkey (the above key/cert structure)
                    131:        string          application
                    132:        uint8           flags
                    133:        string          key_handle
                    134:        string          reserved
                    135:
1.1       djm       136: During key generation, the hardware also returns attestation information
                    137: that may be used to cryptographically prove that a given key is
                    138: hardware-backed. Unfortunately, the protocol required for this proof is
                    139: not privacy-preserving and may be used to identify U2F tokens with at
                    140: least manufacturer and batch number granularity. For this reason, we
                    141: choose not to include this information in the public key or save it by
                    142: default.
                    143:
1.19      djm       144: Attestation information is useful for out-of-band key and certificate
1.20      dtucker   145: registration workflows, e.g. proving to a CA that a key is backed
1.19      djm       146: by trusted hardware before it will issue a certificate. To support this
                    147: case, OpenSSH optionally allows retaining the attestation information
                    148: at the time of key generation. It will take the following format:
1.1       djm       149:
1.19      djm       150:        string          "ssh-sk-attest-v00"
1.1       djm       151:        string          attestation certificate
                    152:        string          enrollment signature
1.19      djm       153:        uint32          reserved flags
                    154:        string          reserved string
                    155:
                    156: OpenSSH treats the attestation certificate and enrollment signatures as
                    157: opaque objects and does no interpretation of them itself.
1.1       djm       158:
                    159: SSH U2F signatures
                    160: ------------------
                    161:
1.9       djm       162: In addition to the message to be signed, the U2F signature operation
1.10      djm       163: requires the key handle and a few additional parameters. The signature
                    164: is signed over a blob that consists of:
1.1       djm       165:
                    166:        byte[32]        SHA256(application)
                    167:        byte            flags (including "user present", extensions present)
                    168:        uint32          counter
                    169:        byte[]          extensions
                    170:        byte[32]        SHA256(message)
                    171:
1.20      dtucker   172: No extensions are yet defined for SSH use. If any are defined in the future,
1.13      djm       173: it will be possible to infer their presence from the contents of the "flags"
                    174: value.
                    175:
1.1       djm       176: The signature returned from U2F hardware takes the following format:
                    177:
                    178:        byte            flags (including "user present")
                    179:        uint32          counter
1.10      djm       180:        byte[]          ecdsa_signature (in X9.62 format).
1.1       djm       181:
                    182: For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
                    183: format data in the pre-authentication attack surface. Therefore, the
                    184: signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
1.8       djm       185: be reformatted to better match the existing signature encoding:
1.1       djm       186:
1.8       djm       187:        string          "sk-ecdsa-sha2-nistp256@openssh.com"
                    188:        string          ecdsa_signature
1.1       djm       189:        byte            flags
                    190:        uint32          counter
                    191:
1.8       djm       192: Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature
                    193: encoding:
                    194:
                    195:        mpint           r
                    196:        mpint           s
1.1       djm       197:
1.4       markus    198: For Ed25519 keys the signature is encoded as:
                    199:
                    200:        string          "sk-ssh-ed25519@openssh.com"
                    201:        string          signature
                    202:        byte            flags
                    203:        uint32          counter
                    204:
1.1       djm       205: ssh-agent protocol extensions
                    206: -----------------------------
                    207:
1.2       naddy     208: ssh-agent requires a protocol extension to support U2F keys. At
1.1       djm       209: present the closest analogue to Security Keys in ssh-agent are PKCS#11
                    210: tokens, insofar as they require a middleware library to communicate with
                    211: the device that holds the keys. Unfortunately, the protocol message used
                    212: to add PKCS#11 keys to ssh-agent does not include any way to send the
                    213: key handle to the agent as U2F keys require.
                    214:
1.2       naddy     215: To avoid this, without having to add wholly new messages to the agent
                    216: protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
                    217: with a new key constraint extension to encode a path to the middleware
1.1       djm       218: library for the key. The format of this constraint extension would be:
                    219:
                    220:        byte            SSH_AGENT_CONSTRAIN_EXTENSION
1.11      djm       221:        string          sk-provider@openssh.com
1.1       djm       222:        string          middleware path
                    223:
                    224: This constraint-based approach does not present any compatibility
                    225: problems.
                    226:
                    227: OpenSSH integration
                    228: -------------------
                    229:
                    230: U2F tokens may be attached via a number of means, including USB and NFC.
                    231: The USB interface is standardised around a HID protocol, but we want to
                    232: be able to support other transports as well as dummy implementations for
1.7       djm       233: regress testing. For this reason, OpenSSH shall support a dynamically-
                    234: loaded middleware libraries to communicate with security keys, but offer
                    235: support for the common case of USB HID security keys internally.
1.1       djm       236:
                    237: The middleware library need only expose a handful of functions:
                    238:
1.21    ! djm       239:        #define SSH_SK_VERSION_MAJOR            0x00050000 /* API version */
1.16      djm       240:        #define SSH_SK_VERSION_MAJOR_MASK       0xffff0000
                    241:
1.1       djm       242:        /* Flags */
                    243:        #define SSH_SK_USER_PRESENCE_REQD       0x01
1.15      djm       244:        #define SSH_SK_USER_VERIFICATION_REQD   0x04
                    245:        #define SSH_SK_RESIDENT_KEY             0x20
1.1       djm       246:
1.3       markus    247:        /* Algs */
                    248:        #define SSH_SK_ECDSA                   0x00
                    249:        #define SSH_SK_ED25519                 0x01
                    250:
1.17      djm       251:        /* Error codes */
                    252:        #define SSH_SK_ERR_GENERAL              -1
                    253:        #define SSH_SK_ERR_UNSUPPORTED          -2
                    254:        #define SSH_SK_ERR_PIN_REQUIRED         -3
1.18      djm       255:        #define SSH_SK_ERR_DEVICE_NOT_FOUND     -4
1.17      djm       256:
1.1       djm       257:        struct sk_enroll_response {
                    258:                uint8_t *public_key;
                    259:                size_t public_key_len;
                    260:                uint8_t *key_handle;
                    261:                size_t key_handle_len;
                    262:                uint8_t *signature;
                    263:                size_t signature_len;
                    264:                uint8_t *attestation_cert;
                    265:                size_t attestation_cert_len;
                    266:        };
                    267:
                    268:        struct sk_sign_response {
                    269:                uint8_t flags;
                    270:                uint32_t counter;
                    271:                uint8_t *sig_r;
                    272:                size_t sig_r_len;
                    273:                uint8_t *sig_s;
                    274:                size_t sig_s_len;
                    275:        };
                    276:
1.16      djm       277:        struct sk_resident_key {
1.17      djm       278:                uint32_t alg;
1.16      djm       279:                size_t slot;
                    280:                char *application;
                    281:                struct sk_enroll_response key;
                    282:        };
                    283:
1.17      djm       284:        struct sk_option {
                    285:                char *name;
                    286:                char *value;
                    287:                uint8_t important;
                    288:        };
                    289:
1.1       djm       290:        /* Return the version of the middleware API */
                    291:        uint32_t sk_api_version(void);
                    292:
                    293:        /* Enroll a U2F key (private key generation) */
1.17      djm       294:        int sk_enroll(uint32_t alg,
                    295:            const uint8_t *challenge, size_t challenge_len,
1.16      djm       296:            const char *application, uint8_t flags, const char *pin,
1.17      djm       297:            struct sk_option **options,
1.1       djm       298:            struct sk_enroll_response **enroll_response);
                    299:
                    300:        /* Sign a challenge */
1.17      djm       301:        int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
1.1       djm       302:            const char *application,
                    303:            const uint8_t *key_handle, size_t key_handle_len,
1.17      djm       304:            uint8_t flags, const char *pin, struct sk_option **options,
1.16      djm       305:            struct sk_sign_response **sign_response);
                    306:
                    307:        /* Enumerate all resident keys */
1.17      djm       308:        int sk_load_resident_keys(const char *pin, struct sk_option **options,
1.16      djm       309:            struct sk_resident_key ***rks, size_t *nrks);
                    310:
                    311: The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
                    312: API change.
1.1       djm       313:
1.17      djm       314: The options may be used to pass miscellaneous options to the middleware
                    315: as a NULL-terminated array of pointers to struct sk_option. The middleware
                    316: may ignore unsupported or unknown options unless the "important" flag is
                    317: set, in which case it should return failure if an unsupported option is
                    318: requested.
                    319:
                    320: At present the following options names are supported:
                    321:
                    322:        "device"
                    323:
                    324:        Specifies a specific FIDO device on which to perform the
                    325:        operation. The value in this field is interpreted by the
                    326:        middleware but it would be typical to specify a path to
                    327:        a /dev node for the device in question.
                    328:
                    329:        "user"
                    330:
                    331:        Specifies the FIDO2 username used when enrolling a key,
                    332:        overriding OpenSSH's default of using an all-zero username.
                    333:
                    334: In OpenSSH, the middleware will be invoked by using a similar mechanism to
1.9       djm       335: ssh-pkcs11-helper to provide address-space containment of the
                    336: middleware from ssh-agent.
1.1       djm       337: