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

Diff for /src/usr.bin/ssh/sshkey.c between version 1.75 and 1.76

version 1.75, 2019/05/20 00:20:35 version 1.76, 2019/06/21 04:21:05
Line 72 
Line 72 
 /* Version identification string for SSH v1 identity files. */  /* Version identification string for SSH v1 identity files. */
 #define LEGACY_BEGIN            "SSH PRIVATE KEY FILE FORMAT 1.1\n"  #define LEGACY_BEGIN            "SSH PRIVATE KEY FILE FORMAT 1.1\n"
   
 int     sshkey_private_serialize_opt(const struct sshkey *key,  /*
    * Constants relating to "shielding" support; protection of keys expected
    * to remain in memory for long durations
    */
   #define SSHKEY_SHIELD_PREKEY_LEN        (16 * 1024)
   #define SSHKEY_SHIELD_CIPHER            "aes256-ctr" /* XXX want AES-EME* */
   #define SSHKEY_SHIELD_PREKEY_HASH       SSH_DIGEST_SHA512
   
   int     sshkey_private_serialize_opt(struct sshkey *key,
     struct sshbuf *buf, enum sshkey_serialize_rep);      struct sshbuf *buf, enum sshkey_serialize_rep);
 static int sshkey_from_blob_internal(struct sshbuf *buf,  static int sshkey_from_blob_internal(struct sshbuf *buf,
     struct sshkey **keyp, int allow_cert);      struct sshkey **keyp, int allow_cert);
Line 580 
Line 588 
         }          }
         if (sshkey_is_cert(k))          if (sshkey_is_cert(k))
                 cert_free(k->cert);                  cert_free(k->cert);
           freezero(k->shielded_private, k->shielded_len);
           freezero(k->shield_prekey, k->shield_prekey_len);
         freezero(k, sizeof(*k));          freezero(k, sizeof(*k));
 }  }
   
Line 1827 
Line 1837 
         return r;          return r;
 }  }
   
   int
   sshkey_is_shielded(struct sshkey *k)
   {
           return k != NULL && k->shielded_private != NULL;
   }
   
   int
   sshkey_shield_private(struct sshkey *k)
   {
           struct sshbuf *prvbuf = NULL;
           u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH];
           struct sshcipher_ctx *cctx = NULL;
           const struct sshcipher *cipher;
           size_t i, enclen = 0;
           struct sshkey *kswap = NULL, tmp;
           int r = SSH_ERR_INTERNAL_ERROR;
   
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
   #endif
           if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
                   r = SSH_ERR_INVALID_ARGUMENT;
                   goto out;
           }
           if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
               ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
                   r = SSH_ERR_INTERNAL_ERROR;
                   goto out;
           }
   
           /* Prepare a random pre-key, and from it an ephemeral key */
           if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) {
                   r = SSH_ERR_ALLOC_FAIL;
                   goto out;
           }
           arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN);
           if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
               prekey, SSHKEY_SHIELD_PREKEY_LEN,
               keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
                   goto out;
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: key+iv\n", __func__);
           sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
               stderr);
   #endif
           if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
               keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0)
                   goto out;
   
           /* Serialise and encrypt the private key using the ephemeral key */
           if ((prvbuf = sshbuf_new()) == NULL) {
                   r = SSH_ERR_ALLOC_FAIL;
                   goto out;
           }
           if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0)
                   goto out;
           if ((r = sshkey_private_serialize_opt(k, prvbuf,
                SSHKEY_SERIALIZE_FULL)) != 0)
                   goto out;
           /* pad to cipher blocksize */
           i = 0;
           while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) {
                   if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0)
                           goto out;
           }
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: serialised\n", __func__);
           sshbuf_dump(prvbuf, stderr);
   #endif
           /* encrypt */
           enclen = sshbuf_len(prvbuf);
           if ((enc = malloc(enclen)) == NULL) {
                   r = SSH_ERR_ALLOC_FAIL;
                   goto out;
           }
           if ((r = cipher_crypt(cctx, 0, enc,
               sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0)
                   goto out;
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: encrypted\n", __func__);
           sshbuf_dump_data(enc, enclen, stderr);
   #endif
   
           /* Make a scrubbed, public-only copy of our private key argument */
           if ((r = sshkey_from_private(k, &kswap)) != 0)
                   goto out;
   
           /* Swap the private key out (it will be destroyed below) */
           tmp = *kswap;
           *kswap = *k;
           *k = tmp;
   
           /* Insert the shielded key into our argument */
           k->shielded_private = enc;
           k->shielded_len = enclen;
           k->shield_prekey = prekey;
           k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN;
           enc = prekey = NULL; /* transferred */
           enclen = 0;
   
           /* success */
           r = 0;
   
    out:
           /* XXX behaviour on error - invalidate original private key? */
           cipher_free(cctx);
           explicit_bzero(enc, enclen);
           explicit_bzero(keyiv, sizeof(keyiv));
           explicit_bzero(&tmp, sizeof(tmp));
           freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN);
           sshkey_free(kswap);
           sshbuf_free(prvbuf);
           return r;
   }
   
   int
   sshkey_unshield_private(struct sshkey *k)
   {
           struct sshbuf *prvbuf = NULL;
           u_char pad, *cp, keyiv[SSH_DIGEST_MAX_LENGTH];
           struct sshcipher_ctx *cctx = NULL;
           const struct sshcipher *cipher;
           size_t i;
           struct sshkey *kswap = NULL, tmp;
           int r = SSH_ERR_INTERNAL_ERROR;
   
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k));
   #endif
           if (!sshkey_is_shielded(k))
                   return 0; /* nothing to do */
   
           if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) {
                   r = SSH_ERR_INVALID_ARGUMENT;
                   goto out;
           }
           if (cipher_keylen(cipher) + cipher_ivlen(cipher) >
               ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) {
                   r = SSH_ERR_INTERNAL_ERROR;
                   goto out;
           }
           /* check size of shielded key blob */
           if (k->shielded_len < cipher_blocksize(cipher) ||
               (k->shielded_len % cipher_blocksize(cipher)) != 0) {
                   r = SSH_ERR_INVALID_FORMAT;
                   goto out;
           }
   
           /* Calculate the ephemeral key from the prekey */
           if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH,
               k->shield_prekey, k->shield_prekey_len,
               keyiv, SSH_DIGEST_MAX_LENGTH)) != 0)
                   goto out;
           if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher),
               keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0)
                   goto out;
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: key+iv\n", __func__);
           sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH),
               stderr);
   #endif
   
           /* Decrypt and parse the shielded private key using the ephemeral key */
           if ((prvbuf = sshbuf_new()) == NULL) {
                   r = SSH_ERR_ALLOC_FAIL;
                   goto out;
           }
           if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0)
                   goto out;
           /* decrypt */
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: encrypted\n", __func__);
           sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr);
   #endif
           if ((r = cipher_crypt(cctx, 0, cp,
               k->shielded_private, k->shielded_len, 0, 0)) != 0)
                   goto out;
   #ifdef DEBUG_PK
           fprintf(stderr, "%s: serialised\n", __func__);
           sshbuf_dump(prvbuf, stderr);
   #endif
           /* Parse private key */
           if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0)
                   goto out;
           /* Check deterministic padding */
           i = 0;
           while (sshbuf_len(prvbuf)) {
                   if ((r = sshbuf_get_u8(prvbuf, &pad)) != 0)
                           goto out;
                   if (pad != (++i & 0xff)) {
                           r = SSH_ERR_INVALID_FORMAT;
                           goto out;
                   }
           }
   
           /* Swap the parsed key back into place */
           tmp = *kswap;
           *kswap = *k;
           *k = tmp;
   
           /* success */
           r = 0;
   
    out:
           cipher_free(cctx);
           explicit_bzero(keyiv, sizeof(keyiv));
           explicit_bzero(&tmp, sizeof(tmp));
           sshkey_free(kswap);
           sshbuf_free(prvbuf);
           return r;
   }
   
 static int  static int
 cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)  cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
 {  {
Line 2323 
Line 2545 
 }  }
   
 int  int
 sshkey_sign(const struct sshkey *key,  sshkey_sign(struct sshkey *key,
     u_char **sigp, size_t *lenp,      u_char **sigp, size_t *lenp,
     const u_char *data, size_t datalen, const char *alg, u_int compat)      const u_char *data, size_t datalen, const char *alg, u_int compat)
 {  {
           int was_shielded = sshkey_is_shielded(key);
           int r2, r = SSH_ERR_INTERNAL_ERROR;
   
         if (sigp != NULL)          if (sigp != NULL)
                 *sigp = NULL;                  *sigp = NULL;
         if (lenp != NULL)          if (lenp != NULL)
                 *lenp = 0;                  *lenp = 0;
         if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)          if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
                 return SSH_ERR_INVALID_ARGUMENT;                  return SSH_ERR_INVALID_ARGUMENT;
           if ((r = sshkey_unshield_private(key)) != 0)
                   return r;
         switch (key->type) {          switch (key->type) {
 #ifdef WITH_OPENSSL  #ifdef WITH_OPENSSL
         case KEY_DSA_CERT:          case KEY_DSA_CERT:
         case KEY_DSA:          case KEY_DSA:
                 return ssh_dss_sign(key, sigp, lenp, data, datalen, compat);                  r = ssh_dss_sign(key, sigp, lenp, data, datalen, compat);
                   break;
         case KEY_ECDSA_CERT:          case KEY_ECDSA_CERT:
         case KEY_ECDSA:          case KEY_ECDSA:
                 return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);                  r = ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
                   break;
         case KEY_RSA_CERT:          case KEY_RSA_CERT:
         case KEY_RSA:          case KEY_RSA:
                 return ssh_rsa_sign(key, sigp, lenp, data, datalen, alg);                  r = ssh_rsa_sign(key, sigp, lenp, data, datalen, alg);
                   break;
 #endif /* WITH_OPENSSL */  #endif /* WITH_OPENSSL */
         case KEY_ED25519:          case KEY_ED25519:
         case KEY_ED25519_CERT:          case KEY_ED25519_CERT:
                 return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);                  r = ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
                   break;
 #ifdef WITH_XMSS  #ifdef WITH_XMSS
         case KEY_XMSS:          case KEY_XMSS:
         case KEY_XMSS_CERT:          case KEY_XMSS_CERT:
                 return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);                  r = ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
                   break;
 #endif /* WITH_XMSS */  #endif /* WITH_XMSS */
         default:          default:
                 return SSH_ERR_KEY_TYPE_UNKNOWN;                  r = SSH_ERR_KEY_TYPE_UNKNOWN;
                   break;
         }          }
           if (was_shielded && (r2 = sshkey_shield_private(key)) != 0)
                   return r2;
           return r;
 }  }
   
 /*  /*
Line 2596 
Line 2832 
 }  }
   
 static int  static int
 default_key_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,  default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp,
     const u_char *data, size_t datalen,      const u_char *data, size_t datalen,
     const char *alg, u_int compat, void *ctx)      const char *alg, u_int compat, void *ctx)
 {  {
Line 2706 
Line 2942 
 }  }
   
 int  int
 sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b,  sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf,
     enum sshkey_serialize_rep opts)      enum sshkey_serialize_rep opts)
 {  {
         int r = SSH_ERR_INTERNAL_ERROR;          int r = SSH_ERR_INTERNAL_ERROR;
           int was_shielded = sshkey_is_shielded(key);
           struct sshbuf *b = NULL;
 #ifdef WITH_OPENSSL  #ifdef WITH_OPENSSL
         const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q;          const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q;
         const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key;          const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key;
 #endif /* WITH_OPENSSL */  #endif /* WITH_OPENSSL */
   
           if ((r = sshkey_unshield_private(key)) != 0)
                   return r;
           if ((b = sshbuf_new()) == NULL)
                   return SSH_ERR_ALLOC_FAIL;
         if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)          if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)
                 goto out;                  goto out;
         switch (key->type) {          switch (key->type) {
Line 2838 
Line 3080 
                 r = SSH_ERR_INVALID_ARGUMENT;                  r = SSH_ERR_INVALID_ARGUMENT;
                 goto out;                  goto out;
         }          }
         /* success */          /*
            * success (but we still need to append the output to buf after
            * possibly re-shielding the private key)
            */
         r = 0;          r = 0;
  out:   out:
           if (was_shielded)
                   r = sshkey_shield_private(key);
           if (r == 0)
                   r = sshbuf_putb(buf, b);
           sshbuf_free(b);
   
         return r;          return r;
 }  }
   
 int  int
 sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)  sshkey_private_serialize(struct sshkey *key, struct sshbuf *b)
 {  {
         return sshkey_private_serialize_opt(key, b,          return sshkey_private_serialize_opt(key, b,
             SSHKEY_SERIALIZE_DEFAULT);              SSHKEY_SERIALIZE_DEFAULT);
Line 3298 
Line 3549 
 #endif /* WITH_OPENSSL */  #endif /* WITH_OPENSSL */
   
 static int  static int
 sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,  sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob,
     const char *passphrase, const char *comment, const char *ciphername,      const char *passphrase, const char *comment, const char *ciphername,
     int rounds)      int rounds)
 {  {
Line 3668 
Line 3919 
 #ifdef WITH_OPENSSL  #ifdef WITH_OPENSSL
 /* convert SSH v2 key in OpenSSL PEM format */  /* convert SSH v2 key in OpenSSL PEM format */
 static int  static int
 sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob,  sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf,
     const char *_passphrase, const char *comment)      const char *_passphrase, const char *comment)
 {  {
           int was_shielded = sshkey_is_shielded(key);
         int success, r;          int success, r;
         int blen, len = strlen(_passphrase);          int blen, len = strlen(_passphrase);
         u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;          u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
         const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;          const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
         char *bptr;          char *bptr;
         BIO *bio = NULL;          BIO *bio = NULL;
           struct sshbuf *blob;
   
         if (len > 0 && len <= 4)          if (len > 0 && len <= 4)
                 return SSH_ERR_PASSPHRASE_TOO_SHORT;                  return SSH_ERR_PASSPHRASE_TOO_SHORT;
         if ((bio = BIO_new(BIO_s_mem())) == NULL)          if ((blob = sshbuf_new()) == NULL)
                 return SSH_ERR_ALLOC_FAIL;                  return SSH_ERR_ALLOC_FAIL;
           if ((bio = BIO_new(BIO_s_mem())) == NULL) {
                   sshbuf_free(blob);
                   return SSH_ERR_ALLOC_FAIL;
           }
           if ((r = sshkey_unshield_private(key)) != 0)
                   goto out;
   
         switch (key->type) {          switch (key->type) {
         case KEY_DSA:          case KEY_DSA:
Line 3712 
Line 3971 
                 goto out;                  goto out;
         r = 0;          r = 0;
  out:   out:
           if (was_shielded)
                   r = sshkey_shield_private(key);
           if (r == 0)
                   r = sshbuf_putb(buf, blob);
           sshbuf_free(blob);
   
         BIO_free(bio);          BIO_free(bio);
         return r;          return r;
 }  }
Line 4024 
Line 4289 
 }  }
 #else  #else
 int  int
 sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,  sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b,
     u_int32_t maxsign, sshkey_printfn *pr)      u_int32_t maxsign, sshkey_printfn *pr)
 {  {
         return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);          return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);

Legend:
Removed from v.1.75  
changed lines
  Added in v.1.76