272 lines
7.2 KiB
C
272 lines
7.2 KiB
C
/* $Id$
|
|
|
|
Part of SWI-Prolog
|
|
|
|
Author: Jan Wielemaker
|
|
E-mail: J.Wielemaker@cs.vu.nl
|
|
WWW: http://www.swi-prolog.org
|
|
Copyright (C): 1985-2009, University of Amsterdam
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#define _ISOC99_SOURCE
|
|
#define USE_SHA256 1
|
|
|
|
#include <SWI-Prolog.h>
|
|
#include "error.h"
|
|
#include "sha1/sha1.h"
|
|
#include "sha1/sha2.h"
|
|
#include "sha1/hmac.h"
|
|
#include <assert.h>
|
|
|
|
static atom_t ATOM_sha1;
|
|
static atom_t ATOM_sha224;
|
|
static atom_t ATOM_sha256;
|
|
static atom_t ATOM_sha384;
|
|
static atom_t ATOM_sha512;
|
|
static atom_t ATOM_algorithm;
|
|
|
|
typedef enum
|
|
{ ALGORITHM_SHA1,
|
|
ALGORITHM_SHA224,
|
|
ALGORITHM_SHA256,
|
|
ALGORITHM_SHA384,
|
|
ALGORITHM_SHA512
|
|
} sha_algorithm;
|
|
|
|
|
|
typedef struct
|
|
{ sha_algorithm algorithm;
|
|
size_t digest_size;
|
|
term_t algorithm_term;
|
|
} optval;
|
|
|
|
#define CONTEXT_MAGIC (~ 0x53484163L)
|
|
|
|
struct context
|
|
{ int magic;
|
|
optval opts;
|
|
union {
|
|
sha1_ctx sha1;
|
|
sha2_ctx sha2;
|
|
} context;
|
|
};
|
|
|
|
static int
|
|
sha_options(term_t options, optval *result)
|
|
{ term_t opts = PL_copy_term_ref(options);
|
|
term_t opt = PL_new_term_ref();
|
|
|
|
/* defaults */
|
|
memset(result, 0, sizeof(*result));
|
|
result->algorithm = ALGORITHM_SHA1;
|
|
result->digest_size = SHA1_DIGEST_SIZE;
|
|
|
|
while(PL_get_list(opts, opt, opts))
|
|
{ atom_t aname;
|
|
int arity;
|
|
|
|
if ( PL_get_name_arity(opt, &aname, &arity) && arity == 1 )
|
|
{ term_t a = PL_new_term_ref();
|
|
|
|
_PL_get_arg(1, opt, a);
|
|
|
|
if ( aname == ATOM_algorithm )
|
|
{ atom_t a_algorithm;
|
|
|
|
result->algorithm_term = a;
|
|
if ( !PL_get_atom(a, &a_algorithm) )
|
|
return pl_error(NULL, 0, NULL, ERR_TYPE, a, "algorithm");
|
|
if ( a_algorithm == ATOM_sha1 )
|
|
{ result->algorithm = ALGORITHM_SHA1;
|
|
result->digest_size = SHA1_DIGEST_SIZE;
|
|
} else if ( a_algorithm == ATOM_sha224 )
|
|
{ result->algorithm = ALGORITHM_SHA224;
|
|
result->digest_size = SHA224_DIGEST_SIZE;
|
|
} else if ( a_algorithm == ATOM_sha256 )
|
|
{ result->algorithm = ALGORITHM_SHA256;
|
|
result->digest_size = SHA256_DIGEST_SIZE;
|
|
} else if ( a_algorithm == ATOM_sha384 )
|
|
{ result->algorithm = ALGORITHM_SHA384;
|
|
result->digest_size = SHA384_DIGEST_SIZE;
|
|
} else if ( a_algorithm == ATOM_sha512 )
|
|
{ result->algorithm = ALGORITHM_SHA512;
|
|
result->digest_size = SHA512_DIGEST_SIZE;
|
|
} else
|
|
return pl_error(NULL, 0, NULL, ERR_DOMAIN, a, "algorithm");
|
|
}
|
|
} else
|
|
{ return pl_error(NULL, 0, NULL, ERR_TYPE, opt, "option");
|
|
}
|
|
}
|
|
|
|
if ( !PL_get_nil(opts) )
|
|
return pl_error("sha_hash", 1, NULL, ERR_TYPE, opts, "list");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
static foreign_t
|
|
pl_sha_hash(term_t from, term_t hash, term_t options)
|
|
{ char *data;
|
|
size_t datalen;
|
|
optval opts;
|
|
unsigned char hval[SHA2_MAX_DIGEST_SIZE];
|
|
|
|
if ( !sha_options(options, &opts) )
|
|
return FALSE;
|
|
|
|
if ( !PL_get_nchars(from, &datalen, &data,
|
|
CVT_ATOM|CVT_STRING|CVT_LIST|CVT_EXCEPTION) )
|
|
return FALSE;
|
|
|
|
if ( opts.algorithm == ALGORITHM_SHA1 )
|
|
{ sha1((unsigned char*)hval,
|
|
(unsigned char*)data, (unsigned long)datalen);
|
|
} else
|
|
{ sha2((unsigned char*)hval, (unsigned long) opts.digest_size,
|
|
(unsigned char*)data, (unsigned long)datalen);
|
|
}
|
|
|
|
return PL_unify_list_ncodes(hash, opts.digest_size, (char*)hval);
|
|
}
|
|
|
|
|
|
static foreign_t
|
|
pl_sha_new_ctx(term_t ctx, term_t options)
|
|
{ struct context c;
|
|
optval *op = &(c.opts);
|
|
|
|
if ( !sha_options(options, op) )
|
|
return FALSE;
|
|
|
|
c.magic = CONTEXT_MAGIC;
|
|
|
|
if ( op->algorithm == ALGORITHM_SHA1 )
|
|
{ sha1_begin(&(c.context.sha1));
|
|
} else
|
|
{ sha2_begin((unsigned long) op->digest_size, &(c.context.sha2));
|
|
}
|
|
|
|
/* NB: the context size depends on the digest size */
|
|
/* (e. g., sha512_ctx is twice as long as sha256_ctx) */
|
|
/* so there're extra data. It will do no harm, though. */
|
|
/* . */
|
|
return PL_unify_string_nchars(ctx, sizeof(c), (char*)&c);
|
|
}
|
|
|
|
|
|
static foreign_t
|
|
pl_sha_hash_ctx(term_t old_ctx, term_t from, term_t new_ctx, term_t hash)
|
|
{ char *data;
|
|
size_t datalen;
|
|
struct context *cp;
|
|
size_t clen;
|
|
unsigned char hval[SHA2_MAX_DIGEST_SIZE];
|
|
|
|
if ( !PL_get_nchars(from, &datalen, &data,
|
|
CVT_ATOM|CVT_STRING|CVT_LIST|CVT_EXCEPTION) )
|
|
return FALSE;
|
|
|
|
if ( !PL_get_string_chars(old_ctx, (char **)&cp, &clen) )
|
|
return FALSE;
|
|
|
|
if ( clen != sizeof (*cp)
|
|
|| cp->magic != CONTEXT_MAGIC ) {
|
|
return pl_error(NULL, 0, "Invalid OldContext passed",
|
|
ERR_DOMAIN, old_ctx, "algorithm");
|
|
}
|
|
|
|
if ( cp->opts.algorithm == ALGORITHM_SHA1 )
|
|
{ sha1_ctx *c1p = &(cp->context.sha1);
|
|
sha1_hash((unsigned char*)data, (unsigned long)datalen, c1p);
|
|
if ( !PL_unify_string_nchars(new_ctx, sizeof(*cp), (char*)cp) )
|
|
return FALSE;
|
|
sha1_end((unsigned char *)hval, c1p);
|
|
} else
|
|
{ sha2_ctx *c1p = &(cp->context.sha2);
|
|
sha2_hash((unsigned char*)data, (unsigned long)datalen, c1p);
|
|
if ( !PL_unify_string_nchars(new_ctx, sizeof(*cp), (char*)cp) )
|
|
return FALSE;
|
|
sha2_end((unsigned char *)hval, c1p);
|
|
}
|
|
|
|
/* . */
|
|
return PL_unify_list_ncodes(hash, cp->opts.digest_size, (char*)hval);
|
|
}
|
|
|
|
|
|
static foreign_t
|
|
pl_hmac_sha(term_t key, term_t data, term_t mac, term_t options)
|
|
{ char *sdata, *skey;
|
|
size_t datalen, keylen;
|
|
optval opts;
|
|
unsigned char digest[SHA2_MAX_DIGEST_SIZE];
|
|
|
|
if ( !PL_get_nchars(key, &keylen, &skey,
|
|
CVT_ATOM|CVT_STRING|CVT_LIST|CVT_EXCEPTION|BUF_RING) )
|
|
return FALSE;
|
|
if ( !PL_get_nchars(data, &datalen, &sdata,
|
|
CVT_ATOM|CVT_STRING|CVT_LIST|CVT_EXCEPTION|BUF_RING) )
|
|
return FALSE;
|
|
|
|
if ( !sha_options(options, &opts) )
|
|
return FALSE;
|
|
|
|
switch(opts.algorithm)
|
|
{ case ALGORITHM_SHA1:
|
|
hmac_sha1((unsigned char*)skey, (unsigned long)keylen,
|
|
(unsigned char*)sdata, (unsigned long)datalen,
|
|
digest, (unsigned long)opts.digest_size);
|
|
break;
|
|
case ALGORITHM_SHA256:
|
|
hmac_sha256((unsigned char*)skey, (unsigned long)keylen,
|
|
(unsigned char*)sdata, (unsigned long)datalen,
|
|
digest, (unsigned long)opts.digest_size);
|
|
break;
|
|
default:
|
|
return pl_error(NULL, 0, "HMAC-SHA only for SHA-1 and SHA-256",
|
|
ERR_DOMAIN, opts.algorithm_term, "algorithm");
|
|
}
|
|
|
|
return PL_unify_list_ncodes(mac, opts.digest_size, (char*)digest);
|
|
}
|
|
|
|
|
|
#define MKATOM(n) ATOM_ ## n = PL_new_atom(#n);
|
|
|
|
install_t
|
|
install_sha4pl()
|
|
{ MKATOM(sha1); /* =160 */
|
|
MKATOM(sha224);
|
|
MKATOM(sha256);
|
|
MKATOM(sha384);
|
|
MKATOM(sha512);
|
|
MKATOM(algorithm);
|
|
|
|
PL_register_foreign("sha_hash", 3, pl_sha_hash, 0);
|
|
PL_register_foreign("sha_new_ctx", 2, pl_sha_new_ctx, 0);
|
|
PL_register_foreign("sha_hash_ctx", 4, pl_sha_hash_ctx, 0);
|
|
PL_register_foreign("hmac_sha", 4, pl_hmac_sha, 0);
|
|
}
|