static void test_random(void) { long long i, j; unsigned char sk1[SCALARBYTES + 16]; unsigned char pk1[BYTES + 16]; unsigned char k1[BYTES + 16]; unsigned char sk2[SCALARBYTES + 16]; unsigned char pk2[BYTES + 16]; unsigned char k2[BYTES + 16]; for (i = 0; i < 16; ++i) { unsaferandombytes(sk1 + i, SCALARBYTES); unsaferandombytes(sk2 + i, SCALARBYTES); if (crypto_scalarmult_curve25519_base(pk1 + i, sk1 + i) != 0) goto fail; pk1[31 + i] |= 128; if (crypto_scalarmult_curve25519_base(pk2 + i, sk2 + i) != 0) goto fail; pk2[31 + i] |= 128; if (crypto_scalarmult_curve25519(k1 + i, sk1 + i, pk2 + i) != 0) goto fail; if (crypto_scalarmult_curve25519(k2 + i, sk2 + i, pk1 + i) != 0) goto fail; for (j = 0; j < BYTES; ++j) if (k1[j + i] != k2[j + i]) goto fail; } return; fail: fail_printdata("sk1", sk1 + i, SCALARBYTES); fail_printdata("sk2", sk2 + i, SCALARBYTES); fail("crypto_scalarmult_curve25519() failure, please report it !!!!!!!!!"); }
int kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int raw) { u_char shared_key[CURVE25519_SIZE]; u_char zero[CURVE25519_SIZE]; int r; crypto_scalarmult_curve25519(shared_key, key, pub); /* Check for all-zero shared secret */ explicit_bzero(zero, CURVE25519_SIZE); if (timingsafe_bcmp(zero, shared_key, CURVE25519_SIZE) == 0) return SSH_ERR_KEY_INVALID_EC_VALUE; #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif if (raw) r = sshbuf_put(out, shared_key, CURVE25519_SIZE); else r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); return r; }
/** * Get a shared secret. * * @param outputSecret an array to place the shared secret in. * @param myPrivateKey * @param herPublicKey * @param logger * @param passwordHash a 32 byte value known to both ends, this must be provably pseudorandom * the first 32 bytes of a sha256 output from hashing a password is ok, * whatever she happens to send me in the Auth field is NOT ok. * If this field is null, the secret will be generated without the password. */ static inline void getSharedSecret(uint8_t outputSecret[32], uint8_t myPrivateKey[32], uint8_t herPublicKey[32], uint8_t passwordHash[32], struct Log* logger) { uint8_t tempBuff[64]; crypto_scalarmult_curve25519(tempBuff, myPrivateKey, herPublicKey); if (passwordHash == NULL) { crypto_core_hsalsa20(outputSecret, keyHashNonce, tempBuff, keyHashSigma); } else { memcpy(&tempBuff[32], passwordHash, 32); crypto_hash_sha256(outputSecret, tempBuff, 64); } #ifdef Log_KEYS uint8_t myPublicKeyHex[65]; printHexPubKey(myPublicKeyHex, myPrivateKey); uint8_t herPublicKeyHex[65]; printHexKey(herPublicKeyHex, herPublicKey); uint8_t passwordHashHex[65]; printHexKey(passwordHashHex, passwordHash); uint8_t outputSecretHex[65] = "NULL"; printHexKey(outputSecretHex, outputSecret); Log_keys4(logger, "Generated a shared secret:\n" " myPublicKey=%s\n" " herPublicKey=%s\n" " passwordHash=%s\n" " outputSecret=%s\n", myPublicKeyHex, herPublicKeyHex, passwordHashHex, outputSecretHex); #endif }
int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out) { u_char shared_key[CURVE25519_SIZE]; int r; #ifdef USING_WOLFSSL int ret, ssize = CURVE25519_SIZE; ret = wolfSSL_EC25519_shared_key(shared_key, &ssize, key, CURVE25519_SIZE, pub, CURVE25519_SIZE); if (ret != 1 || ssize != CURVE25519_SIZE) fatal("%s: wolfSSL_EC25519_shared_key failed", __func__); #else /* Check for all-zero public key */ explicit_bzero(shared_key, CURVE25519_SIZE); if (timingsafe_bcmp(pub, shared_key, CURVE25519_SIZE) == 0) return SSH_ERR_KEY_INVALID_EC_VALUE; crypto_scalarmult_curve25519(shared_key, key, pub); #endif /* USING_WOLFSSL */ #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif sshbuf_reset(out); r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); return r; }
static void test_vector(void) { long long j; unsigned char r[BYTES]; if (crypto_scalarmult_curve25519(r, d, S) != 0) fail("crypto_scalarmult_curve25519() failure"); for (j = 0; j < BYTES; ++j) if (r[j] != R[j]) fail("crypto_scalarmult_curve25519() failure"); }
void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) { static const u_char basepoint[CURVE25519_SIZE] = {9}; arc4random_buf(key, CURVE25519_SIZE); crypto_scalarmult_curve25519(pub, key, basepoint); }
int crypto_dh_curve25519( unsigned char *s, const unsigned char *pk, const unsigned char *sk ) { crypto_scalarmult_curve25519(s,sk,pk); return 0; }
int crypto_box_beforenm( unsigned char *k, const unsigned char *pk, const unsigned char *sk ) { unsigned char s[32]; crypto_scalarmult_curve25519(s,sk,pk); return crypto_core_hsalsa20(k,n,s,sigma); }
int main() { int i; crypto_scalarmult_curve25519(k,alicesk,bobpk); for (i = 0;i < 32;++i) { if (i > 0) printf(","); else printf(" "); printf("0x%02x",(unsigned int) k[i]); if (i % 8 == 7) printf("\n"); } return 0; }
int crypto_box_curve25519xchacha20poly1305_beforenm(unsigned char *k, const unsigned char *pk, const unsigned char *sk) { unsigned char s[32]; if (crypto_scalarmult_curve25519(s, sk, pk) != 0) { return -1; } return crypto_core_hchacha20(k, n, s, NULL); }
int crypto_box_curve25519xsalsa20poly1305_beforenm(unsigned char *k, const unsigned char *pk, const unsigned char *sk) { static const unsigned char zero[16] = { 0 }; unsigned char s[32]; if (crypto_scalarmult_curve25519(s, sk, pk) != 0) { return -1; } return crypto_core_hsalsa20(k, zero, s, NULL); }
void kexc25519_shared_key(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], Buffer *out) { u_char shared_key[CURVE25519_SIZE]; crypto_scalarmult_curve25519(shared_key, key, pub); #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif buffer_clear(out); buffer_put_bignum2_from_string(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); }
int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out) { u_char shared_key[CURVE25519_SIZE]; int r; crypto_scalarmult_curve25519(shared_key, key, pub); #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif sshbuf_reset(out); r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); return r; }
void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) { #ifdef USING_WOLFSSL int ret, psize = CURVE25519_SIZE, ksize = CURVE25519_SIZE; ret = wolfSSL_EC25519_generate_key(key, &ksize, pub, &psize); if (ret != 1 || ksize != CURVE25519_SIZE || psize != CURVE25519_SIZE) fatal("%s: wolfSSL_EC25519_generate_key failed (%d,%d,%d)", __func__, ret, ksize, psize); #else static const u_char basepoint[CURVE25519_SIZE] = {9}; arc4random_buf(key, CURVE25519_SIZE); crypto_scalarmult_curve25519(pub, key, basepoint); #endif /* USING_WOLFSSL */ }
STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, const uint8_t *basepoint) { uint8_t bp[CURVE25519_PUBKEY_LEN]; int r; memcpy(bp, basepoint, CURVE25519_PUBKEY_LEN); /* Clear the high bit, in case our backend foolishly looks at it. */ bp[31] &= 0x7f; #ifdef USE_CURVE25519_DONNA r = curve25519_donna(output, secret, bp); #elif defined(USE_CURVE25519_NACL) r = crypto_scalarmult_curve25519(output, secret, bp); #else #error "No implementation of curve25519 is available." #endif memwipe(bp, 0, sizeof(bp)); return r; }
static void test_scalarmult(void) { long long i, j; unsigned char outpk[BYTES]; for (i = 0; i < BYTES; ++i) pk[i] = basepoint[i]; checksum_zero(); for (i = 0; i < 1080; ++i) { pk[31] |= 128; if (crypto_scalarmult_curve25519(outpk, skdata[i], pk) != 0) { fail_printdata("pk", pk, BYTES); fail_printdata("sk", skdata[i], SCALARBYTES); fail("crypto_scalarmult_curve25519() failure, please report it !!!!!!!!!"); } checksum(outpk, BYTES); for (j = 0; j < BYTES; ++j) pk[j] = outpk[j]; } fail_whenbadchecksum(test_scalarmult_checksum); }
int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out) { u_char shared_key[CURVE25519_SIZE]; int r; /* Check for all-zero public key */ explicit_bzero(shared_key, CURVE25519_SIZE); if (timingsafe_bcmp(pub, shared_key, CURVE25519_SIZE) == 0) return SSH_ERR_KEY_INVALID_EC_VALUE; crypto_scalarmult_curve25519(shared_key, key, pub); #ifdef DEBUG_KEXECDH dump_digest("shared secret", shared_key, CURVE25519_SIZE); #endif sshbuf_reset(out); r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); explicit_bzero(shared_key, CURVE25519_SIZE); return r; }
/** * Get a shared secret. * * @param outputSecret an array to place the shared secret in. * @param myPrivateKey * @param herPublicKey * @param logger * @param passwordHash a 32 byte value known to both ends, this must be provably pseudorandom * the first 32 bytes of a sha256 output from hashing a password is ok, * whatever she happens to send me in the Auth field is NOT ok. * If this field is null, the secret will be generated without the password. */ static inline void getSharedSecret(uint8_t outputSecret[32], uint8_t myPrivateKey[32], uint8_t herPublicKey[32], uint8_t passwordHash[32], struct Log* logger) { if (passwordHash == NULL) { crypto_box_curve25519xsalsa20poly1305_beforenm(outputSecret, herPublicKey, myPrivateKey); } else { union { struct { uint8_t key[32]; uint8_t passwd[32]; } components; uint8_t bytes[64]; } buff; crypto_scalarmult_curve25519(buff.components.key, myPrivateKey, herPublicKey); Bits_memcpyConst(buff.components.passwd, passwordHash, 32); crypto_hash_sha256(outputSecret, buff.bytes, 64); } #ifdef Log_KEYS uint8_t myPublicKeyHex[65]; printHexPubKey(myPublicKeyHex, myPrivateKey); uint8_t herPublicKeyHex[65]; printHexKey(herPublicKeyHex, herPublicKey); uint8_t passwordHashHex[65]; printHexKey(passwordHashHex, passwordHash); uint8_t outputSecretHex[65] = "NULL"; printHexKey(outputSecretHex, outputSecret); Log_keys(logger, "Generated a shared secret:\n" " myPublicKey=%s\n" " herPublicKey=%s\n" " passwordHash=%s\n" " outputSecret=%s\n", myPublicKeyHex, herPublicKeyHex, passwordHashHex, outputSecretHex); #endif }
int kex_ecdh(struct per_session_data__sshd *pss, uint8_t *reply, uint32_t *plen) { uint8_t pri_key[64], temp[64], payload_sig[64 + 32], a, *lp, kbi[64]; struct lws_kex *kex = pss->kex; struct lws_genhash_ctx ctx; unsigned long long smlen; uint8_t *p = reply + 5; uint32_t be, kbi_len; uint8_t servkey[256]; char keyt[33]; int r, c; r = get_gen_server_key_25519(pss, servkey, sizeof(servkey)); if (!r) { lwsl_err("%s: Failed to get or gen server key\n", __func__); return 1; } r = ed25519_key_parse(servkey, r, keyt, sizeof(keyt), pss->K_S /* public key */, pri_key); if (r) { lwsl_notice("%s: server key parse failed: %d\n", __func__, r); return 1; } keyt[32] = '\0'; lwsl_info("Server key type: %s\n", keyt); /* * 1) Generate ephemeral key pair [ eph_pri_key | kex->Q_S ] * 2) Compute shared secret. * 3) Generate and sign exchange hash. * * 1) A 32 bytes private key should be generated for each new * connection, using a secure PRNG. The following actions * must be done on the private key: * * mysecret[0] &= 248; * mysecret[31] &= 127; * mysecret[31] |= 64; */ lws_get_random(pss->vhd->context, kex->eph_pri_key, LWS_SIZE_EC25519); kex->eph_pri_key[0] &= 248; kex->eph_pri_key[31] &= 127; kex->eph_pri_key[31] |= 64; /* * 2) The public key is calculated using the cryptographic scalar * multiplication: * * const unsigned char privkey[32]; * unsigned char pubkey[32]; * * crypto_scalarmult (pubkey, privkey, basepoint); */ crypto_scalarmult_curve25519(kex->Q_S, kex->eph_pri_key, basepoint); a = 0; for (r = 0; r < sizeof(kex->Q_S); r++) a |= kex->Q_S[r]; if (!a) { lwsl_notice("all zero pubkey\n"); return SSH_DISCONNECT_KEY_EXCHANGE_FAILED; } /* * The shared secret, k, is defined in SSH specifications to be a big * integer. This number is calculated using the following procedure: * * X is the 32 bytes point obtained by the scalar multiplication of * the other side's public key and the local private key scalar. */ crypto_scalarmult_curve25519(pss->K, kex->eph_pri_key, kex->Q_C); /* * The whole 32 bytes of the number X are then converted into a big * integer k. This conversion follows the network byte order. This * step differs from RFC5656. */ kbi_len = lws_mpint_rfc4251(kbi, pss->K, LWS_SIZE_EC25519, 1); /* * The exchange hash H is computed as the hash of the concatenation of * the following: * * string V_C, the client's identification string (CR and LF * excluded) * string V_S, the server's identification string (CR and LF * excluded) * string I_C, the payload of the client's SSH_MSG_KEXINIT * string I_S, the payload of the server's SSH_MSG_KEXINIT * string K_S, the host key * mpint Q_C, exchange value sent by the client * mpint Q_S, exchange value sent by the server * mpint K, the shared secret * * However there are a lot of unwritten details in the hash * definition... */ if (lws_genhash_init(&ctx, LWS_GENHASH_TYPE_SHA256)) { lwsl_notice("genhash init failed\n"); return 1; } if (_genhash_update_len(&ctx, pss->V_C, strlen(pss->V_C))) goto hash_probs; if (_genhash_update_len(&ctx, pss->vhd->ops->server_string, /* aka V_S */ strlen(pss->vhd->ops->server_string))) goto hash_probs; if (_genhash_update_len(&ctx, kex->I_C, kex->I_C_payload_len)) goto hash_probs; if (_genhash_update_len(&ctx, kex->I_S, kex->I_S_payload_len)) goto hash_probs; /* * K_S (host public key) * * sum of name + key lengths and headers * name length: name * key length: key * ---> */ lws_p32((uint8_t *)&be, 8 + strlen(keyt) + LWS_SIZE_EC25519); if (lws_genhash_update(&ctx, (void *)&be, 4)) goto hash_probs; if (_genhash_update_len(&ctx, keyt, strlen(keyt))) goto hash_probs; if (_genhash_update_len(&ctx, pss->K_S, LWS_SIZE_EC25519)) goto hash_probs; /* <---- */ if (_genhash_update_len(&ctx, kex->Q_C, LWS_SIZE_EC25519)) goto hash_probs; if (_genhash_update_len(&ctx, kex->Q_S, LWS_SIZE_EC25519)) goto hash_probs; if (lws_genhash_update(&ctx, kbi, kbi_len)) goto hash_probs; if (lws_genhash_destroy(&ctx, temp)) goto hash_probs; /* * Sign the 32-byte SHA256 "exchange hash" in temp * The signature is itself 64 bytes */ smlen = LWS_SIZE_EC25519 + 64; if (crypto_sign_ed25519(payload_sig, &smlen, temp, LWS_SIZE_EC25519, pri_key)) return 1; #if 0 l = LWS_SIZE_EC25519; n = crypto_sign_ed25519_open(temp, &l, payload_sig, smlen, pss->K_S); lwsl_notice("own sig sanity check says %d\n", n); #endif /* sig [64] and payload [32] concatenated in payload_sig * * The server then responds with the following * * uint32 packet length (exl self + mac) * byte padding len * byte SSH_MSG_KEX_ECDH_REPLY * string server public host key and certificates (K_S) * string Q_S (exchange value sent by the server) * string signature of H * padding */ *p++ = SSH_MSG_KEX_ECDH_REPLY; /* server public host key and certificates (K_S) */ lp = p; p +=4; lws_sized_blob(&p, keyt, strlen(keyt)); lws_sized_blob(&p, pss->K_S, LWS_SIZE_EC25519); lws_p32(lp, p - lp - 4); /* Q_S (exchange value sent by the server) */ lws_sized_blob(&p, kex->Q_S, LWS_SIZE_EC25519); /* signature of H */ lp = p; p +=4; lws_sized_blob(&p, keyt, strlen(keyt)); lws_sized_blob(&p, payload_sig, 64); lws_p32(lp, p - lp - 4); /* end of message */ lws_pad_set_length(pss, reply, &p, &pss->active_keys_stc); *plen = p - reply; if (!pss->active_keys_stc.valid) memcpy(pss->session_id, temp, LWS_SIZE_EC25519); /* RFC4253 7.2: * * The key exchange produces two values: a shared secret K, * and an exchange hash H. Encryption and authentication * keys are derived from these. The exchange hash H from the * first key exchange is additionally used as the session * identifier, which is a unique identifier for this connection. * It is used by authentication methods as a part of the data * that is signed as a proof of possession of a private key. * Once computed, the session identifier is not changed, * even if keys are later re-exchanged. * * The hash alg used in the KEX must be used for key derivation. * * 1) Initial IV client to server: * * HASH(K || H || "A" || session_id) * * (Here K is encoded as mpint and "A" as byte and session_id * as raw data. "A" means the single character A, ASCII 65). * * */ for (c = 0; c < 3; c++) { kex_ecdh_dv(kex->keys_next_cts.key[c], LWS_SIZE_CHACHA256_KEY, kbi, kbi_len, temp, 'A' + (c * 2), pss->session_id); kex_ecdh_dv(kex->keys_next_stc.key[c], LWS_SIZE_CHACHA256_KEY, kbi, kbi_len, temp, 'B' + (c * 2), pss->session_id); } explicit_bzero(temp, sizeof(temp)); return 0; hash_probs: lws_genhash_destroy(&ctx, NULL); return 1; }
SODIUM_EXPORT int crypto_scalarmult_curve25519_donna_c64(unsigned char *q, const unsigned char *n, const unsigned char *p) { return crypto_scalarmult_curve25519(q, n, p); }