A client sends to me a message signed with a private key, type ECDSA secp256R1. I'm in possession of a leaf certificate, in DER format, provided by the client. In addition, I also have the raw message and a sha256 digest of the msg.
I have created a struct where to store all the required info for the verification, with the idea of providing a public API in my application:
struct SignatureVerifyData {
unsigned char *msg;
unsigned char *hash; // digest sha256 of msg
unsigned char *cert; // leaf cert in DER
unsigned char *signature;
size_t msg_len;
size_t hash_len;
size_t cert_len;
size_t signature_len;
};
I'm reading the ecdsa.c example from MbedTLS, but in this case the cert is generated in the same example, I can use mbedtls_x509_crt_parse_der() to load my leaf cert, but then, should I to move it to a mbedtls_ecdsa_context object to use with mbedtls_ecdsa_read_signature()?
Should I use other way to load the leaf cert?
Confused on how to use the group and point objects, or if I need to use them at all.
#define MBEDTLS_HAVE_ASM
#define MBEDTLS_HAVE_TIME
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_GCM_C
#define MBEDTLS_MD_C
#define MBEDTLS_NET_C
#define MBEDTLS_OID_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_SHA224_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA384_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_AES_ROM_TABLES
#define MBEDTLS_MPI_MAX_SIZE 48 // 384-bit EC curve = 48 bytes
#define MBEDTLS_ECP_WINDOW_SIZE 2
#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0
#define MBEDTLS_ECP_NIST_OPTIM
#define MBEDTLS_ENTROPY_MAX_SOURCES 2
#define MBEDTLS_SSL_CIPHERSUITES \
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, \
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
#define MBEDTLS_SSL_IN_CONTENT_LEN 1024
#define MBEDTLS_SSL_OUT_CONTENT_LEN 1024
#include "mbedtls/check_config.h"
mbedtls_x509_crt_parse_der constructs an object of type mbedtls_x509_crt. This structure has a field called pk which contains the public key. Call mbedtls_pk_verify to verify the signature.
Here's the general idea of the code to parse the certificate, calculate the hash and verify the signature. Untested code, typed directly into my browser. Error checking omitted, be sure to check that all the function calls succeed.
#include <mbedtls/md.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509_crt.h>
mbedtls_x509_crt crt;
mbedtls_x509_init(&crt);
mbedtls_x509_crt_parse_der(&crt, cert, cert_len);
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
hash_len = mbedtls_md_get_size(md_info);
hash = malloc(hash_len);
mbedtls_md(md_info, msg, msg_len, hash);
mbedtls_pk_verify(&crt->pk, MBEDTLS_MD_SHA256, hash, hash_len, signature, signature_len);
mbedtls_x509_free(&crt);
free(hash);
GnuTLS suffered drastic changes as I see in the examples mentioned in the following websites with respect to my version of code that previously worked based on almost the same examples as before:
Echo server with X.509 authentication
Client example with X.509 certificate support
the problem is that before you had to use the authority certificate for the client program, now you need the certificate and the key that was only used on the server side. What is this drastic change? In the end which side belongs as private and public among the generated certificates? (according to the example, I have to replace gnutls_certificate_set_x509_trust_file by gnutls_certificate_set_x509_key_file)
What are the lines with the parameters for the openssl command that I have to use to generate all the necessary certificates in this example of Server and Client?
It is also very confusing the issue of the file names for the generated certificates, sometimes it is .pem and sometimes .crt/.key, tell you how I should create these names exactly (if you think some of this is wrong).
Because of the updates of GnuTLS it has stopped working and now the Handshake fails, causing the server to fail the handshake and in the client continue believing that the connection is fine.
this is the code:
Server:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <gnutls/gnutls.h>
int main(){
const char*txterr;
int lsck;
gnutls_certificate_credentials_t xcred;
gnutls_priority_t pcache;
gnutls_session_t sesion;
sockaddr_in _cli;
//INICIO GLOBAL
//if(!gnutls_check_version("3.6.15"))return -1;
if(!gnutls_check_version("3.5.10"))return -1;
if(gnutls_global_init()<0)return -2;
if(gnutls_certificate_allocate_credentials(&xcred)<0)return -3;
if(gnutls_certificate_set_x509_trust_file(xcred,"cert/ca.crt",GNUTLS_X509_FMT_PEM)<0)return -4;
//if(gnutls_certificate_set_x509_crl_file(xcred,"cert/revocar.crl",GNUTLS_X509_FMT_PEM)<0)return -5;
if(gnutls_certificate_set_x509_key_file(
xcred,"cert/serv.crt","cert/serv.key",GNUTLS_X509_FMT_PEM)<0)return -6;
//if(gnutls_certificate_set_ocsp_status_request_file(xcred,"reqocsp.der",0)<0)return -4;
if(gnutls_priority_init(&pcache,"PERFORMANCE:%SERVER_PRECEDENCE",&txterr)<0){printf("TLS: %s\n",txterr);return -7;}
if(gnutls_certificate_set_known_dh_params(xcred,GNUTLS_SEC_PARAM_MEDIUM)<0)return -8;
//ABRE PUERTO SERVIDOR
lsck=socket(AF_INET,SOCK_STREAM,0);
if(lsck==-1){lsck=0;printf("Imposible usar Puerto\n");return -1;}
{int optval=1;setsockopt(lsck,SOL_SOCKET,SO_REUSEADDR,(void*)&optval,sizeof(int));}
//Convierte el puerto en Escucha
{
struct sockaddr_in dsrv;
dsrv.sin_family=AF_INET;
dsrv.sin_port=htons(9080);
dsrv.sin_addr.s_addr=INADDR_ANY;
if(bind(lsck,(struct sockaddr*)&dsrv,sizeof(dsrv))!=0)
{printf("Imposible bindear Puerto\n");return -2;}
if(listen(lsck,10)!=0)
{printf("Imposible abrir Puerto\n");return -3;}
}
printf("Abriendo Puerto\n");
//ESCUCHA PUERTO
if(gnutls_init(&sesion,GNUTLS_SERVER)<0)return -6;
if(gnutls_priority_set(sesion,pcache)<0)return -6;
if(gnutls_credentials_set(sesion,GNUTLS_CRD_CERTIFICATE,xcred)<0)return -6;
gnutls_certificate_server_set_request(sesion,GNUTLS_CERT_IGNORE);
gnutls_handshake_set_timeout(sesion,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
int _scli=sizeof(sockaddr_in);
int nsck=accept(lsck,(sockaddr*)&_cli,(socklen_t*)&_scli);
if(nsck<0)return -3;
gnutls_transport_set_int(sesion,nsck);
int tm;
do{
tm=gnutls_handshake(sesion);
}while(tm<0&&gnutls_error_is_fatal(tm)==0);
if(tm<0){close(nsck);gnutls_deinit(sesion);return -6;}
printf("Entrando Usuario: %x:%d\n",_cli.sin_addr,_cli.sin_port);
//CIFRA PUERTO CLIENTE
gnutls_datum_t out;
gnutls_session_t tls_sesion;
if(gnutls_init(&tls_sesion,GNUTLS_CLIENT)<0)return -6;
if(gnutls_server_name_set(tls_sesion,GNUTLS_NAME_DNS,"blackrook.ddns.net",strlen("blackrook.ddns.net"))<0)return -7;
if(gnutls_set_default_priority(tls_sesion)<0)return -8;
int ret=gnutls_priority_set_direct(tls_sesion,"NORMAL",&txterr);
if(ret<0){
switch(ret){
case GNUTLS_E_INVALID_REQUEST:
printf("Error tipo %s\n",txterr);
return -9;
}
}
if(gnutls_credentials_set(tls_sesion,GNUTLS_CRD_CERTIFICATE,xcred)<0)return -10;
gnutls_session_set_verify_cert(tls_sesion,"blackrook.ddns.net",0);
//Toma el FD Socket
gnutls_transport_set_int(tls_sesion,nsck);
gnutls_handshake_set_timeout(tls_sesion,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
do{ret=gnutls_handshake(tls_sesion);
}while(ret<0&&!gnutls_error_is_fatal(ret));
if(ret<0){
gnutls_certificate_type_t type;
int status;
switch(ret){
case GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR:
type=gnutls_certificate_type_get(tls_sesion);
status=gnutls_session_get_verify_cert_status(tls_sesion);
if(gnutls_certificate_verification_status_print(status,type,&out,0)<0)return -255;
printf("TLS: cert verify output: %s\n",out.data);
gnutls_free(out.data);
}
printf("TLS: Handshake fallado: %s\n",gnutls_strerror(ret));
return -11;
}{
char*desc=gnutls_session_get_desc(tls_sesion);
printf("TLS: Session info: %s\n",desc);
gnutls_free(desc);
}
//Envia mensaje cifrado
char*msg="Este es canal abierto.";
gnutls_record_send(tls_sesion,msg,strlen(msg)+1);
//CIERRA
if(gnutls_bye(tls_sesion,GNUTLS_SHUT_WR)<0)printf("TLS: Falla cerrando\n");
close(nsck);
gnutls_deinit(tls_sesion);
close(lsck);
gnutls_certificate_free_credentials(xcred);
gnutls_priority_deinit(pcache);
gnutls_global_deinit();
return 0;
}
Cliente:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <gnutls/gnutls.h>
int main(){
int fd;
const char*txterr;
gnutls_certificate_credentials_t xcred;
gnutls_session_t tls_sesion;
//INICIA TLS
if(!gnutls_check_version("3.5.10"))return -1;
if(gnutls_global_init()<0)return -2;
if(gnutls_certificate_allocate_credentials(&xcred)<0)return -3;
if(gnutls_certificate_set_x509_system_trust(xcred)<0)return -4;
if(gnutls_certificate_set_x509_trust_file(xcred,"cert/ca.crt",GNUTLS_X509_FMT_PEM)<0)return -5;
//if(gnutls_certificate_set_x509_key_file(xcred,"cert/cert.pem","cert/key.pem",GNUTLS_X509_FMT_PEM)<0)return -5;
//ABRE SOCKET
fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){printf("Imposible abrir nuevo E/S\n");return -1;}
struct addrinfo hints,*serv,*p;
char*zip="blackrook.ddns.net",*zprt="9080";
memset(&hints,0,sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
int i,ip;
if(i=getaddrinfo(zip,zprt,&hints,&serv)){
printf("Conexion no establecida\n");return 0;}
for(p=serv;p!=0;p=p->ai_next){
if(p->ai_addr->sa_family==AF_INET){
ip=((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr;
if(connect(fd,p->ai_addr,p->ai_addrlen)==-1)continue;
freeaddrinfo(serv);
printf("Conexion establecida\n");goto JmpConti;
}
}freeaddrinfo(serv);printf("Conexion no establecida2\n");return 0;
JmpConti:
//CIFRA PUERTO
gnutls_datum_t out;
if(gnutls_init(&tls_sesion,GNUTLS_CLIENT)<0)return -6;
if(gnutls_server_name_set(tls_sesion,GNUTLS_NAME_DNS,"blackrook.ddns.net",strlen("blackrook.ddns.net"))<0)return -7;
if(gnutls_set_default_priority(tls_sesion)<0)return -8;
int ret=gnutls_priority_set_direct(tls_sesion,"NORMAL",&txterr);
if(ret<0){
switch(ret){
case GNUTLS_E_INVALID_REQUEST:
printf("Error tipo %s\n",txterr);
return -9;
}
}
if(gnutls_credentials_set(tls_sesion,GNUTLS_CRD_CERTIFICATE,xcred)<0)return -10;
gnutls_session_set_verify_cert(tls_sesion,"blackrook.ddns.net",0);
//Toma el FD Socket
gnutls_transport_set_int(tls_sesion,fd);
gnutls_handshake_set_timeout(tls_sesion,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
do{ret=gnutls_handshake(tls_sesion);
}while(ret<0&&!gnutls_error_is_fatal(ret));
if(ret<0){
printf("RET:%d\n",ret);
gnutls_certificate_type_t type;
int status;
switch(ret){
case GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR:
type=gnutls_certificate_type_get(tls_sesion);
status=gnutls_session_get_verify_cert_status(tls_sesion);
if(gnutls_certificate_verification_status_print(status,type,&out,0)<0)return -255;
printf("TLS: cert verify output: %s\n",out.data);
gnutls_free(out.data);
}
printf("TLS: Handshake fallado: %s\n",gnutls_strerror(ret));
return -11;
}{
char*desc=gnutls_session_get_desc(tls_sesion);
printf("TLS: Session info: %s\n",desc);
gnutls_free(desc);
}
//Recibe mensaje
char msg[16387];long max=16387,nres;
sleep(4);
nres=gnutls_record_recv(tls_sesion,msg,max);
printf("R:%s\n",msg);
//CIERRA PUERTO
if(gnutls_bye(tls_sesion,GNUTLS_SHUT_RDWR)<0)printf("TLS: Falla cerrando\n");
close(fd);
gnutls_deinit(tls_sesion);
gnutls_certificate_free_credentials(xcred);
gnutls_global_deinit();
return 0;
}
I working on little ECC crypto problem.
The goal is to write a program in C or bash, which will take as input a hash composed of 128 characters in hexadecimal
(Example: 8A9A35145C4EA5260DF9972C804FE2D3F9F3D7A2AC01A6BEB21C82BB30957B3952273AC9166B90C1207347A925780F84A1D2359E7AA05201C674D2B9746FCA07)
and which will generate from the input hash a private key and a public key of type Elliptic Curve and display the key pair generated.
Can someone clarify for me the problem. I can't understand why we need a hash(or any string) to generate a pair key, as I found In many online solution like this one ther's no need to give a hash. Maybe is it a parphase ? Maybe It's the curve key or somthing similar.
I think all we need is to do something like this for the private key:
openssl ecparam -genkey -noout -out myprivatekey.pem
and for the public key generation:
openssl -ec -in myprivatekey.pem -pubout -out mypublickey.pem
The question is : why we need an input a hash composed of 128 to generate our pair keys? Is it a passphrase for security reason? how made the trick with openssl ?
You could use a hash if you've got some kind of input binary value which you need to convert to a key.
You can use a hash as input for a private key. To convert it you should first convert it to a number and then perform a calculation modulo n on it, where n is the order of the ECC domain parameters. The resulting value can be called s Then you can calculate the public key out of it by performing s * G, i.e. point multiplication with the base point.
OpenSSL is not a low level crypto library, so you'd have to program it, possibly using the OpenSSL API and the BN (big number) library that comes with it. It is not that tricky, but if you're still talking about 128 characters instead of 64 bytes then you may have a lot of learning to do.
In fact this is my own code, you can improve it and edit the solution bellow:
// gcc -Wall ecdsapubkey.c -o ecdsapubkey -lcrypto
#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>
int main()
{
EC_KEY *eckey = NULL;
EC_POINT *pub_key = NULL;
const EC_GROUP *group = NULL;
BIGNUM *start;
BIGNUM *res;
BN_CTX *ctx;
start = BN_new();
ctx = BN_CTX_new(); // ctx is an optional buffer to save time from allocating and deallocating memory whenever required
res = start;
BN_hex2bn(&res,"8A9A35145C4EA5260DF9972C804FE2D3F9F3D7A2AC01A6BEB21C82BB30957B3952273AC9166B90C1207347A925780F84A1D2359E7AA05201C674D2B9746FCA07");
eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
group = EC_KEY_get0_group(eckey);
pub_key = EC_POINT_new(group);
printf("private key : "); BN_print_fp(stdout, res); printf("\n");
EC_KEY_set_private_key(eckey, res);
/* pub_key is a new uninitialized `EC_POINT*`. priv_key res is a `BIGNUM*`. */
if (!EC_POINT_mul(group, pub_key, res, NULL, NULL, ctx))
printf("Error at EC_POINT_mul.\n");
EC_KEY_set_public_key(eckey, pub_key);
char *cc = EC_POINT_point2hex(group, pub_key, 4, ctx);
char *c=cc;
int i;
printf("public key : ");
for (i=0; i<130; i++) // 1 byte 0x42, 32 bytes for X coordinate, 32 bytes for Y coordinate
{
printf("%c", *c++);
}
printf("\n");
BN_CTX_free(ctx);
free(cc);
return 0;
}
I connected my ultimate gps breakout to my raspberry pi using the USB to TTL serial cable. Using C code I can easily connect to and read NMEA sentences from the GPS. But when I write configuration commands such as PMTK220 to set the update rate, they are ignored. I should get back a PMTK_ACK reporting success or failure, but it is not forthcoming. The problem also occurs when I use terminal windows. ie. I run:
while (true) do cat -A /dev/ttyUSB0 ; done
in one terminal, and get a stream of $GPGGA, $GPGSA, $GPRMC etc messages. In another terminal I run:
echo "$PMTK220,200*2C\r\n" > /dev/ttyUSB0
The NMEA messages continue but there is no PMTK001 coming back. Any ideas?
Ok, here's some code (inspired by gpsd source) that demonstrates sending a configuration message and getting an acknowledgement:
#define _BSD_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <semaphore.h>
#include <string.h>
#ifndef CRTSCTS
# ifdef CNEW_RTSCTS
# define CRTSCTS CNEW_RTSCTS
# else
# define CRTSCTS 0
# endif /* CNEW_RTSCTS */
#endif /* !CRTSCTS */
int main (int argc, char **argv) {
int const baudr = B9600, mode = O_RDWR | O_NONBLOCK | O_NOCTTY;
int const fd = open("/dev/ttyUSB0", mode);
assert (fd != -1);
ioctl(fd, (unsigned long)TIOCEXCL);
struct termios ttyOld, ttyNew;
assert(tcgetattr(fd, &ttyOld) != -1);
ttyNew = ttyOld;
ttyNew.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
ttyNew.c_cflag |= CREAD | CLOCAL | CS8;
ttyNew.c_iflag = ttyNew.c_oflag = 0;
ttyNew.c_lflag = ICANON;
cfsetispeed(&ttyNew, baudr);
cfsetospeed(&ttyNew, baudr);
assert(tcsetattr(fd, TCSANOW, &ttyNew) != -1);
tcflush(fd, TCIOFLUSH);
usleep(200000);
tcflush(fd, TCIOFLUSH);
int const oldfl = fcntl(fd, F_GETFL);
assert (oldfl != -1);
fcntl(fd, F_SETFL, oldfl & ~O_NONBLOCK);
tcdrain(fd);
printf("port opened baudr=%d mode=%d i=%d o=%d c=%d l=%d fl=%d\n", baudr, mode, ttyNew.c_iflag, ttyNew.c_oflag, ttyNew.c_cflag, ttyNew.c_lflag, oldfl);
unsigned char buf[2048];
int count = 0;
while(++count < 20) {
if (count == 4) {
char const *const cmd = "$PMTK220,200*2C\r\n";
int const n = strlen(cmd);
assert(write(fd, cmd, n) == n);
tcdrain(fd);
printf("wrote command %d: %s\n", n, cmd);
}
int const n = read(fd, buf, sizeof(buf));
buf[(n >= sizeof(buf)) ? (sizeof(buf) - 1) : n] = 0;
printf(buf);
}
tcsetattr(fd, TCSANOW, &ttyOld);
close(fd);
}
with results:
pi#pi01 ~/c $ ./test
port opened baudr=13 mode=2306 i=0 o=0 c=3261 l=2 fl=2050
$GPGGA,175748.089,,,,,0,00,,,M,,M,,*71
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,175748.089,V,,,,,0.00,0.00,260715,,,N*43
wrote command 17: $PMTK220,200*2C
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$PMTK001,220,2*31
$GPGGA,175749.089,,,,,0,00,,,M,,M,,*70
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,01,11,,,31*7A
$GPRMC,175749.089,V,,,,,0.00,0.00,260715,,,N*42
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,175750.089,,,,,0,00,,,M,,M,,*78
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,175750.089,V,,,,,0.00,0.00,260715,,,N*4A
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,175751.089,,,,,0,00,,,M,,M,,*79
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,175751.089,V,,,,,0.00,0.00,260715,,,N*4B
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,175752.089,,,,,0,00,,,M,,M,,*7A
pi#pi01 ~/c $
Okay, I spent the last couple of days researching this, and I can't believe Apache's natively supported hashing functions are that outdated.
I discovered a couple of ways to do this, which are mod_perl and mod_authnz_external, both of which are too slow, because apache runs that whenever any object inside a protected directory is called. That means that a user may have to be authenticated hundreds of times in a single session.
Has anyone ever managed to get Apache to use something that's more secure than MD5 and SHA-1 without moving authentication away from Apache? Salted SHA-2 would be a real bonus.
Thanks!
If you're on a GNU/Linux system with a version of glibc2 released in the last 5 or so years, you can modify htpasswd's crypt() implementation to prepend "$6$" to the salt, and then it'd be as simple as:
# htpasswd -d -c .htpasswd someusername
When the salt starts with "$6$", glibc2 will use salted SHA-512, with the up to 16 characters after that being the salt, in the range [a-zA-Z0-9./].
See man 3 crypt.
I'm not aware of any patch to support this, but it should be a simple one.
EDIT: I'd also like to mention that one round of even salted SHA-512 is breakable if your attacker is determined enough. I'd recommend, and am using in most things I've been able to edit, 128000 rounds of PBKDF2 with HMAC-SHA512, but this would be a very extensive edit, unless you want to link htpasswd against openssl, which has a PKCS5_PBKDF2_HMAC() function.
EDIT 2: Also, using openssl to do strong hashing isn't hard, if you're interested:
abraxas ~ # cat pbkdf2.c
#include <string.h>
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#define PBKDF2_SALT_PREFIX "$pbkdf2sha512$"
#define PBKDF2_SALT_PREFIX_LENGTH strlen(PBKDF2_SALT_PREFIX)
#define PBKDF2_PRF_ALGORITHM EVP_sha512()
#define PBKDF2_DIGEST_LENGTH SHA512_DIGEST_LENGTH
#define PBKDF2_SALT_LENGTH 32
#define PBKDF2_RESULT_LENGTH PBKDF2_SALT_PREFIX_LENGTH + (2 * PBKDF2_DIGEST_LENGTH) + PBKDF2_SALT_LENGTH + 2
#define PBKDF2_ROUNDS 128000
void hash_password(const char* pass, const unsigned char* salt, char* result)
{
unsigned int i;
static unsigned char digest[PBKDF2_DIGEST_LENGTH];
memcpy(result, PBKDF2_SALT_PREFIX, PBKDF2_SALT_PREFIX_LENGTH);
memcpy(result + PBKDF2_SALT_PREFIX_LENGTH, salt, PBKDF2_SALT_LENGTH);
result[PBKDF2_SALT_PREFIX_LENGTH + PBKDF2_SALT_LENGTH] = '$';
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, PBKDF2_SALT_LENGTH, PBKDF2_ROUNDS, PBKDF2_PRF_ALGORITHM, PBKDF2_DIGEST_LENGTH, digest);
for (i = 0; i < sizeof(digest); i++)
sprintf(result + PBKDF2_SALT_PREFIX_LENGTH + PBKDF2_SALT_LENGTH + 1 + (i * 2), "%02x", 255 & digest[i]);
}
int main(void)
{
char result[PBKDF2_RESULT_LENGTH];
char pass[] = "password";
unsigned char salt[] = "178556d2988b6f833f239cd69bc07ed3";
printf("Computing PBKDF2(HMAC-SHA512, '%s', '%s', %d, %d) ...\n", pass, salt, PBKDF2_ROUNDS, PBKDF2_DIGEST_LENGTH);
memset(result, 0, PBKDF2_RESULT_LENGTH);
hash_password(pass, salt, result);
printf("Result: %s\n", result);
return 0;
}
abraxas ~ # gcc -Wall -Wextra -O3 -lssl pbkdf2.c -o pbkdf2
abraxas ~ # time ./pbkdf2
Computing PBKDF2(HMAC-SHA512, 'password', '178556d2988b6f833f239cd69bc07ed3', 128000, 64) ...
Result: $pbkdf2sha512$178556d2988b6f833f239cd69bc07ed3$3acb79896ce3e623c3fac32f91d4421fe360fcdacfb96ee3460902beac26807d28aca4ed01394de2ea37b363ab86ba448286eaf21e1d5b316149c0b9886741a7
real 0m0.320s
user 0m0.319s
sys 0m0.001s
abraxas ~ #