]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Modernize the hash module
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 24 Feb 2024 01:19:18 +0000 (19:19 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 24 Feb 2024 01:19:18 +0000 (19:19 -0600)
It seems nowadays libcrypto wants us to

- perform "explicit [algorithm] fetching,"
- cache algorithms to improve performance,
- and stop hardcoding algorithms in the code (which largely doesn't
  really apply in our RFC-bound situation).

https://www.openssl.org/docs/man3.0/man7/crypto.html#ALGORITHM-FETCHING

So fetch explicitely, cache algorithms and try to keep hardcoding at a
minimum.

Also, the code was seemingly unaware that hash size depends on
algorithm, so this was often validated weakly.

19 files changed:
src/asn1/signed_data.c
src/crypto/hash.c
src/crypto/hash.h
src/extension.c
src/extension.h
src/main.c
src/object/certificate.c
src/object/manifest.c
src/rrdp.c
src/slurm/db_slurm.h
src/slurm/slurm_loader.c
test/Makefile.am
test/crypto/hash_test.c [new file with mode: 0644]
test/line_file_test.c
test/resources/line_file/core.txt [moved from test/line_file/core.txt with 100% similarity]
test/resources/line_file/empty.txt [moved from test/line_file/empty.txt with 100% similarity]
test/resources/line_file/error.txt [moved from test/line_file/error.txt with 100% similarity]
test/resources/lorem-ipsum.txt [new file with mode: 0644]
test/rrdp_test.c

index 301ae23de095f133a42e955953a809c95cea3b0c..068d3b5dd21cc1be7441a3540681884d0c742035 100644 (file)
@@ -157,8 +157,9 @@ validate_message_digest_attribute(CMSAttributeValue_t *value,
        if (error)
                return error;
 
-       error = hash_validate_octet_string("sha256", digest, eci->eContent);
-       if (error)
+       error = hash_validate(hash_get_sha256(), eci->eContent->buf,
+           eci->eContent->size, digest->buf, digest->size);
+       if (error > 0)
                pr_val_err("The content's hash does not match the Message-Digest Attribute.");
 
        ASN_STRUCT_FREE(asn_DEF_MessageDigest, digest);
index a6e05b6ad51e01684e5368443f96bea381c1037e..740621ccd41f940c3fed604906b048868690c56d 100644 (file)
 #include "crypto/hash.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <openssl/evp.h>
 
 #include "alloc.h"
-#include "common.h"
 #include "file.h"
 #include "log.h"
-#include "asn1/oid.h"
 
-static int
-get_md(char const *algorithm, EVP_MD const **result)
-{
+/*
+ * TODO (fine) Delete this structure (use md directly) once OpenSSL < 3 support
+ * is dropped.
+ */
+struct hash_algorithm {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       EVP_MD *md;
+#else
        EVP_MD const *md;
+#endif
+       size_t size;
+       char const *name;
+};
+
+/*
+ * EVP_sha256() and EVP_sha1() are now mildly deprecated ("present for
+ * compatibility with OpenSSL before version 3.0").
+ *
+ * This is because they want to encourage explicit fetching, but also because
+ * they want us to stop hardcoding the algorithms in the code.
+ *
+ * But we're RFC-bound to use these algorithms, so we only want the explicit
+ * fetching part. (Which is done during hash_setup().)
+ */
+static struct hash_algorithm sha1;
+static struct hash_algorithm sha256;
 
-       md = EVP_get_digestbyname(algorithm);
-       if (md == NULL) {
-               pr_val_err("Unknown message digest %s", algorithm);
-               return -EINVAL;
+int
+hash_setup(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       sha1.md = EVP_MD_fetch(NULL, "SHA1", NULL);
+       if (sha1.md == NULL)
+               return pr_op_err("This version of libcrypto does not seem to support SHA1.");
+       sha1.size = EVP_MD_get_size(sha1.md);
+       sha1.name = EVP_MD_get0_name(sha1.md);
+
+       sha256.md = EVP_MD_fetch(NULL, "SHA256", NULL);
+       if (sha256.md == NULL) {
+               EVP_MD_free(sha1.md);
+               return pr_op_err("This version of libcrypto does not seem to support SHA256.");
        }
+       sha256.size = EVP_MD_get_size(sha256.md);
+       sha256.name = EVP_MD_get0_name(sha256.md);
+
+#else
+       sha1.md = EVP_get_digestbyname("sha1");
+       if (sha1.md == NULL)
+               return pr_op_err("This version of libcrypto does not seem to support SHA1.");
+       sha1.size = EVP_MD_size(sha1.md);
+       sha1.name = EVP_MD_name(sha1.md);
+
+       sha256.md = EVP_get_digestbyname("sha256");
+       if (sha256.md == NULL)
+               return pr_op_err("This version of libcrypto does not seem to support SHA256.");
+       sha256.size = EVP_MD_size(sha256.md);
+       sha256.name = EVP_MD_name(sha256.md);
+
+#endif
 
-       *result = md;
        return 0;
 }
 
-static bool
-hash_matches(unsigned char const *expected, size_t expected_len,
-    unsigned char const *actual, unsigned int actual_len)
+void
+hash_teardown(void)
 {
-       return (expected_len == actual_len)
-           && (memcmp(expected, actual, expected_len) == 0);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       EVP_MD_free(sha256.md);
+       EVP_MD_free(sha1.md);
+#endif
 }
 
-static int
-hash_file(struct rpki_uri *uri, unsigned char *result, unsigned int *result_len)
+struct hash_algorithm const *
+hash_get_sha1(void)
+{
+       return &sha1;
+}
+
+struct hash_algorithm const *
+hash_get_sha256(void)
 {
-       return hash_local_file(uri_get_local(uri), result, result_len);
+       return &sha256;
 }
 
 int
-hash_local_file(char const *uri, unsigned char *result,
-    unsigned int *result_len)
+hash_file(struct hash_algorithm const *algorithm, char const *filename,
+    unsigned char *result, size_t *result_size)
 {
-       EVP_MD const *md;
        FILE *file;
        struct stat stat;
        unsigned char *buffer;
        size_t consumed;
        EVP_MD_CTX *ctx;
+       unsigned int hash_size;
        int error;
 
-       error = get_md("sha256", &md);
-       if (error)
-               return error;
-
-       error = file_open(uri, &file, &stat);
+       error = file_open(filename, &file, &stat);
        if (error)
                return error;
 
@@ -63,7 +115,7 @@ hash_local_file(char const *uri, unsigned char *result,
        if (ctx == NULL)
                enomem_panic();
 
-       if (!EVP_DigestInit_ex(ctx, md, NULL)) {
+       if (!EVP_DigestInit_ex(ctx, algorithm->md, NULL)) {
                error = val_crypto_err("EVP_DigestInit_ex() failed");
                goto end;
        }
@@ -84,8 +136,17 @@ hash_local_file(char const *uri, unsigned char *result,
 
        } while (!feof(file));
 
-       if (!EVP_DigestFinal_ex(ctx, result, result_len))
+       if (!EVP_DigestFinal_ex(ctx, result, &hash_size)) {
                error = val_crypto_err("EVP_DigestFinal_ex() failed");
+               goto end;
+       }
+       if (hash_size != algorithm->size) {
+               error = pr_op_err("libcrypto returned a %s hash sized %u bytes.",
+                   algorithm->name, hash_size);
+       }
+
+       if (result_size)
+               *result_size = hash_size;
 
 end:
        EVP_MD_CTX_free(ctx);
@@ -94,144 +155,84 @@ end:
        return error;
 }
 
-/**
- * Computes the hash of the file @uri, and compares it to @expected (The
- * "expected" hash).
- *
- * Returns:
- *   0 if no errors happened and the hashes match, or the hash doesn't match
- *     but there's an incidence to ignore such error.
- * < 0 if there was an error that can't be ignored.
- * > 0 if there was an error but it can be ignored (file not found and there's
- *     an incidence to ignore this).
- */
 int
-hash_validate_mft_file(struct rpki_uri *uri, BIT_STRING_t const *expected)
+hash_validate_file(struct hash_algorithm const *algorithm, struct rpki_uri *uri,
+    unsigned char const *expected, size_t expected_len)
 {
        unsigned char actual[EVP_MAX_MD_SIZE];
-       unsigned int actual_len;
+       size_t actual_len;
        int error;
 
-       if (expected->bits_unused != 0)
-               return pr_val_err("Hash string has unused bits.");
-
-       do {
-               error = hash_file(uri, actual, &actual_len);
-               if (!error)
-                       break;
-
-               if (error == EACCES || error == ENOENT) {
-                       if (incidence(INID_MFT_FILE_NOT_FOUND,
-                           "File '%s' listed at manifest doesn't exist.",
-                           uri_val_get_printable(uri)))
-                               return -EINVAL;
-
-                       return error;
-               }
-               /* Any other error (crypto, enomem, file read) */
-               return ENSURE_NEGATIVE(error);
-       } while (0);
-
-       if (!hash_matches(expected->buf, expected->size, actual, actual_len)) {
-               return incidence(INID_MFT_FILE_HASH_NOT_MATCH,
-                   "File '%s' does not match its manifest hash.",
-                   uri_val_get_printable(uri));
-       }
-
-       return 0;
-}
-
-/**
- * Computes the hash of the file @uri, and compares it to @expected HASH of
- * @expected_len. Returns 0 if no errors happened and the hashes match.
- */
-int
-hash_validate_file(struct rpki_uri *uri, unsigned char const *expected,
-    size_t expected_len)
-{
-       unsigned char actual[EVP_MAX_MD_SIZE];
-       unsigned int actual_len;
-       int error;
-
-       error = hash_file(uri, actual, &actual_len);
+       error = hash_file(algorithm, uri_get_local(uri), actual, &actual_len);
        if (error)
                return error;
 
-       if (!hash_matches(expected, expected_len, actual, actual_len)) {
-               return pr_val_err("File '%s' does not match its expected hash.",
-                   uri_val_get_printable(uri));
-       }
+       if (expected_len != actual_len)
+               goto fail;
+       if (memcmp(expected, actual, expected_len) != 0)
+               goto fail;
 
        return 0;
+
+fail:
+       return pr_val_err("File '%s' does not match its expected hash.",
+           uri_val_get_printable(uri));
 }
 
 static int
-hash_buffer(char const *algorithm,
-    unsigned char const *content, size_t content_len,
-    unsigned char *hash, unsigned int *hash_len)
+hash_buffer(struct hash_algorithm const *algorithm,
+    unsigned char const *content, size_t content_len, unsigned char *hash)
 {
-       EVP_MD const *md;
        EVP_MD_CTX *ctx;
-       int error;
-
-       error = get_md(algorithm, &md);
-       if (error)
-               return error;
+       unsigned int actual_len;
 
        ctx = EVP_MD_CTX_new();
        if (ctx == NULL)
                enomem_panic();
 
-       if (!EVP_DigestInit_ex(ctx, md, NULL)
-           || !EVP_DigestUpdate(ctx, content, content_len)
-           || !EVP_DigestFinal_ex(ctx, hash, hash_len)) {
-               error = val_crypto_err("Buffer hashing failed");
+       if (!EVP_DigestInit_ex(ctx, algorithm->md, NULL) ||
+           !EVP_DigestUpdate(ctx, content, content_len) ||
+           !EVP_DigestFinal_ex(ctx, hash, &actual_len)) {
+               EVP_MD_CTX_free(ctx);
+               return val_crypto_err("Buffer hashing failed");
        }
 
        EVP_MD_CTX_free(ctx);
-       return error;
+
+       if (actual_len != algorithm->size)
+               pr_crit("libcrypto returned a %s hash sized %u bytes.",
+                   algorithm->name, actual_len);
+
+       return 0;
 }
 
-/*
- * Returns 0 if @data's hash is @expected. Returns error code otherwise.
- */
 int
-hash_validate(char const *algorithm,
-    unsigned char const *expected, size_t expected_len,
-    unsigned char const *data, size_t data_len)
+hash_validate(struct hash_algorithm const *algorithm, unsigned char const *data,
+    size_t data_len, unsigned char const *expected, size_t expected_len)
 {
        unsigned char actual[EVP_MAX_MD_SIZE];
-       unsigned int actual_len;
        int error;
 
-       error = hash_buffer(algorithm, data, data_len, actual, &actual_len);
+       error = hash_buffer(algorithm, data, data_len, actual);
        if (error)
                return error;
 
-       return hash_matches(expected, expected_len, actual, actual_len)
-           ? 0
-           : -EINVAL;
+       if (expected_len != algorithm->size)
+               return EINVAL;
+       if (memcmp(expected, actual, expected_len) != 0)
+               return EINVAL;
+
+       return 0;
 }
 
-int
-hash_validate_octet_string(char const *algorithm,
-    OCTET_STRING_t const *expected,
-    OCTET_STRING_t const *data)
+char const *
+hash_get_name(struct hash_algorithm const *algorithm)
 {
-       return hash_validate(algorithm, expected->buf, expected->size,
-           data->buf, data->size);
+       return algorithm->name;
 }
 
-/*
- * Hash the @str using the specified @algorithm, setting the result at the
- * @result buffer and its length at @result_len.
- * 
- * Return 0 on success, any other value means error.
- */
-int
-hash_str(char const *algorithm, char const *str, unsigned char *result,
-    unsigned int *result_len)
+size_t
+hash_get_size(struct hash_algorithm const *algorithm)
 {
-       return hash_buffer(algorithm, (unsigned char const *) str, strlen(str),
-           result, result_len);
+       return algorithm->size;
 }
index f27fc336f4a477db64abcef3b7215699779b3ef9..6426b1a36ec2ed6a204d9d4b183d2546d749d170 100644 (file)
@@ -1,18 +1,26 @@
 #ifndef SRC_HASH_H_
 #define SRC_HASH_H_
 
+#include <openssl/evp.h>
 #include "types/uri.h"
-#include "asn1/asn1c/BIT_STRING.h"
 
-int hash_validate_mft_file(struct rpki_uri *uri, BIT_STRING_t const *);
-int hash_validate_file(struct rpki_uri *, unsigned char const *, size_t);
-int hash_validate(char const *, unsigned char const *, size_t,
-    unsigned char const *, size_t);
-int hash_validate_octet_string(char const *, OCTET_STRING_t const*,
-    OCTET_STRING_t const *);
+struct hash_algorithm;
+
+int hash_setup(void);
+void hash_teardown(void);
+
+struct hash_algorithm const *hash_get_sha1(void);
+struct hash_algorithm const *hash_get_sha256(void);
 
-int hash_local_file(char const *, unsigned char *, unsigned int *);
+int hash_file(struct hash_algorithm const *, char const *, unsigned char *,
+    size_t *);
+
+int hash_validate_file(struct hash_algorithm const *, struct rpki_uri *,
+    unsigned char const *, size_t);
+int hash_validate(struct hash_algorithm const *, unsigned char const *, size_t,
+    unsigned char const *, size_t);
 
-int hash_str(char const *, char const *, unsigned char *, unsigned int *);
+char const *hash_get_name(struct hash_algorithm const *);
+size_t hash_get_size(struct hash_algorithm const *);
 
 #endif /* SRC_HASH_H_ */
index 11d3e3c32b7b8239acbaee1fda9fc5b336ea7ea7..97cc6c852c024ff6f8b79a040f235bf3573387e5 100644 (file)
@@ -242,7 +242,7 @@ cannot_decode(struct extension_metadata const *meta)
  * Otherwise returns error code.
  */
 int
-validate_public_key_hash(X509 *cert, ASN1_OCTET_STRING *hash)
+validate_public_key_hash(X509 *cert, ASN1_OCTET_STRING *hash, char const *hash_name)
 {
        X509_PUBKEY *pubkey;
        const unsigned char *spk;
@@ -290,20 +290,16 @@ validate_public_key_hash(X509 *cert, ASN1_OCTET_STRING *hash)
        if (!ok)
                return val_crypto_err("X509_PUBKEY_get0_param() returned %d", ok);
 
-       /* Hash the SPK, compare SPK hash with the SKI */
-       if (hash->length < 0 || SIZE_MAX < hash->length) {
-               return pr_val_err("%s length (%d) is out of bounds. (0-%zu)",
-                   ext_ski()->name, hash->length, SIZE_MAX);
-       }
+       /* FIXME the max limit needs to be a lot less than SIZE_MAX. */
        if (spk_len < 0 || SIZE_MAX < spk_len) {
-               return pr_val_err("Subject Public Key length (%d) is out of bounds. (0-%zu)",
+               return pr_val_err("The Subject Public Key length (%d) is out of bounds. (0-%zu)",
                    spk_len, SIZE_MAX);
        }
 
-       error = hash_validate("sha1", hash->data, hash->length, spk, spk_len);
-       if (error) {
+       error = hash_validate(hash_get_sha1(), spk, spk_len, hash->data, hash->length);
+       if (error > 0) {
                pr_val_err("The Subject Public Key's hash does not match the %s.",
-                   ext_ski()->name);
+                   hash_name);
        }
 
        return error;
@@ -339,7 +335,7 @@ handle_aki(X509_EXTENSION *ext, void *arg)
                goto end;
        }
 
-       error = validate_public_key_hash(parent, aki->keyid);
+       error = validate_public_key_hash(parent, aki->keyid, "AKI");
 
 end:
        AUTHORITY_KEYID_free(aki);
index 33a4022d39429cf103ca6aab6ea68f062219ba7a..6dada3b89ec4d6520ab3269093c199660bdd4437 100644 (file)
@@ -44,7 +44,7 @@ int handle_extensions(struct extension_handler *,
     STACK_OF(X509_EXTENSION) const *);
 
 int cannot_decode(struct extension_metadata const *);
-int validate_public_key_hash(X509 *, ASN1_OCTET_STRING *);
+int validate_public_key_hash(X509 *, ASN1_OCTET_STRING *, char const *);
 int handle_aki(X509_EXTENSION *, void *);
 
 #endif /* SRC_EXTENSION_H_ */
index 62ac29defa6f9228fa8b9273c3263c3b1690ac6e..88c56379f121128d44dd1a86ec373a21008f3508 100644 (file)
@@ -5,6 +5,7 @@
 #include "log.h"
 #include "nid.h"
 #include "thread_var.h"
+#include "crypto/hash.h"
 #include "http/http.h"
 #include "incidence/incidence.h"
 #include "rtr/rtr.h"
@@ -149,9 +150,12 @@ main(int argc, char **argv)
        error = extension_init();
        if (error)
                goto revert_nid;
-       error = http_init();
+       error = hash_setup();
        if (error)
                goto revert_nid;
+       error = http_init();
+       if (error)
+               goto revert_hash;
 
        error = relax_ng_init();
        if (error)
@@ -178,6 +182,8 @@ revert_relax_ng:
        relax_ng_cleanup();
 revert_http:
        http_cleanup();
+revert_hash:
+       hash_teardown();
 revert_nid:
        nid_destroy();
 revert_config:
index e3194d4889e56ed58672db367db6d6b6e04ab691..407c6e9f4becb0fdea4daa516dd6422e6b303b6f 100644 (file)
@@ -1263,7 +1263,7 @@ handle_ski_ca(X509_EXTENSION *ext, void *arg)
        if (ski == NULL)
                return cannot_decode(ext_ski());
 
-       error = validate_public_key_hash(arg, ski);
+       error = validate_public_key_hash(arg, ski, "SKI");
 
        ASN1_OCTET_STRING_free(ski);
        return error;
@@ -1282,7 +1282,7 @@ handle_ski_ee(X509_EXTENSION *ext, void *arg)
                return cannot_decode(ext_ski());
 
        args = arg;
-       error = validate_public_key_hash(args->cert, ski);
+       error = validate_public_key_hash(args->cert, ski, "SKI");
        if (error)
                goto end;
 
index 3d1739d89002e518855bd32cb63b0cc7d4aea2c0..4ee6c1c1da1bea6508ab354d0440c3afe1850279 100644 (file)
@@ -188,6 +188,63 @@ validate_manifest(struct Manifest *manifest)
        return 0;
 }
 
+/**
+ * Computes the hash of the file @uri, and compares it to @expected (The
+ * "expected" hash).
+ *
+ * Returns:
+ *   0 if no errors happened and the hashes match, or the hash doesn't match
+ *     but there's an incidence to ignore such error.
+ * < 0 if there was an error that can't be ignored.
+ * > 0 if there was an error but it can be ignored (file not found and there's
+ *     an incidence to ignore this).
+ */
+static int
+hash_validate_mft_file(struct rpki_uri *uri, BIT_STRING_t const *expected)
+{
+       struct hash_algorithm const *algorithm;
+       size_t hash_size;
+       unsigned char actual[EVP_MAX_MD_SIZE];
+       int error;
+
+       algorithm = hash_get_sha256();
+       hash_size = hash_get_size(algorithm);
+
+       if (expected->size != hash_size)
+               return pr_val_err("%s string has bogus size: %zu",
+                   hash_get_name(algorithm), expected->size);
+       if (expected->bits_unused != 0)
+               return pr_val_err("Hash string has unused bits.");
+
+       /*
+        * TODO (#82) This is atrocious. Implement RFC 9286, and probably reuse
+        * hash_validate_file().
+        */
+
+       error = hash_file(algorithm, uri_get_local(uri), actual, NULL);
+       if (error) {
+               if (error == EACCES || error == ENOENT) {
+                       /* FIXME .................. */
+                       if (incidence(INID_MFT_FILE_NOT_FOUND,
+                           "File '%s' listed at manifest doesn't exist.",
+                           uri_val_get_printable(uri)))
+                               return -EINVAL;
+
+                       return error;
+               }
+               /* Any other error (crypto, file read) */
+               return ENSURE_NEGATIVE(error);
+       }
+
+       if (memcmp(expected->buf, actual, hash_size) != 0) {
+               return incidence(INID_MFT_FILE_HASH_NOT_MATCH,
+                   "File '%s' does not match its manifest hash.",
+                   uri_val_get_printable(uri));
+       }
+
+       return 0;
+}
+
 static int
 build_rpp(struct Manifest *mft, struct rpki_uri *notif,
     struct rpki_uri *mft_uri, struct rpp **pp)
index a8251b6e3cdc988f18b36319a9208503d99e5bb8..e285335aedb165c5334d501ea673dc567a23a5ba 100644 (file)
@@ -152,7 +152,8 @@ update_notification_cleanup(struct update_notification *file)
 static int
 validate_hash(struct file_metadata *meta)
 {
-       return hash_validate_file(meta->uri, meta->hash, meta->hash_len);
+       return hash_validate_file(hash_get_sha256(), meta->uri, meta->hash,
+           meta->hash_len);
 }
 
 /* Left trim @from, setting the result at @result pointer */
index 5dd988fb4f2c02f5583d983caa8b85f9ae67a521..2882cd0d71c51d542c68686515a1bbbf0e90d5cb 100644 (file)
@@ -30,8 +30,12 @@ struct slurm_bgpsec {
 };
 
 struct slurm_file_csum {
+       /*
+        * Actual length is SHA256_DIGEST_LENGTH, but the documentation of
+        * EVP_DigestFinal_ex() doesn't say we can allocate less.
+        */
        unsigned char csum[EVP_MAX_MD_SIZE];
-       unsigned int csum_len;
+       size_t csum_len;
        SLIST_ENTRY(slurm_file_csum) next;
 };
 
index 6562a4692a20f90c4d19bed832058b83eb94792e..2c0abd0400ab0e06ba64e935d73e140f94400ced 100644 (file)
@@ -132,7 +132,7 @@ __slurm_load_checksums(char const *location, void *arg)
 
        csum = pmalloc(sizeof(struct slurm_file_csum));
 
-       if (hash_local_file(location, csum->csum, &csum->csum_len) != 0) {
+       if (hash_file(hash_get_sha256(), location, csum->csum, &csum->csum_len) != 0) {
                free(csum);
                return pr_op_err("Calculating slurm hash");
        }
index 6b528020f3d29d7141a146c523d54d07d57e6491..b9d1a415178d26cf3bef5e67d8b857e6f3b5a877 100644 (file)
@@ -26,6 +26,7 @@ check_PROGRAMS  = address.test
 check_PROGRAMS += cache.test
 check_PROGRAMS += db_table.test
 check_PROGRAMS += deltas_array.test
+check_PROGRAMS += hash.test
 check_PROGRAMS += line_file.test
 check_PROGRAMS += pb.test
 check_PROGRAMS += pdu_handler.test
@@ -53,6 +54,9 @@ db_table_test_LDADD = ${MY_LDADD}
 deltas_array_test_SOURCES = rtr/db/deltas_array_test.c
 deltas_array_test_LDADD = ${MY_LDADD}
 
+hash_test_SOURCES = crypto/hash_test.c
+hash_test_LDADD = ${MY_LDADD}
+
 line_file_test_SOURCES = line_file_test.c
 line_file_test_LDADD = ${MY_LDADD}
 
@@ -93,9 +97,10 @@ xml_test_SOURCES = xml_test.c
 xml_test_LDADD = ${MY_LDADD} ${XML2_LIBS}
 
 EXTRA_DIST  = mock.c mock.h
-EXTRA_DIST += line_file/core.txt
-EXTRA_DIST += line_file/empty.txt
-EXTRA_DIST += line_file/error.txt
+EXTRA_DIST += resources/lorem-ipsum.txt
+EXTRA_DIST += resources/line_file/core.txt
+EXTRA_DIST += resources/line_file/empty.txt
+EXTRA_DIST += resources/line_file/error.txt
 EXTRA_DIST += rtr/db/rtr_db_mock.c
 EXTRA_DIST += tal/lacnic.tal
 EXTRA_DIST += xml/notification.xml
diff --git a/test/crypto/hash_test.c b/test/crypto/hash_test.c
new file mode 100644 (file)
index 0000000..ac284db
--- /dev/null
@@ -0,0 +1,110 @@
+#include <check.h>
+#include <stdlib.h>
+
+#include "alloc.c"
+#include "common.c"
+#include "file.c"
+#include "mock.c"
+#include "data_structure/path_builder.c"
+#include "types/uri.c"
+#include "crypto/hash.c"
+
+/* Actually mostly tests libcrypto's sanity, not Fort's. */
+START_TEST(test_hash)
+{
+       static unsigned char FORT_SHA1[] = {
+               0xe5, 0x79, 0x65, 0xf7, 0x36, 0xd2, 0x9d, 0x43, 0xde, 0x05,
+               0xf7, 0x02, 0x86, 0xa7, 0xf4, 0xcc, 0x5b, 0x74, 0x8c, 0xa7
+       };
+       static unsigned char FORT_SHA256[] = {
+               0xb6, 0x9f, 0xee, 0xa9, 0xef, 0xa8, 0x61, 0x5c, 0xd4, 0x91,
+               0x95, 0x7b, 0x7e, 0xf8, 0x28, 0xef, 0xb4, 0x10, 0xc2, 0xdd,
+               0x67, 0x6c, 0xa0, 0x63, 0x75, 0x9a, 0x68, 0x9a, 0xf4, 0xfe,
+               0xf9, 0xb1
+       };
+
+       static unsigned char FILE_SHA1[] = {
+               0xea, 0x3c, 0xa1, 0xc6, 0xe2, 0x3a, 0x70, 0x1a, 0xe8, 0x97,
+               0xec, 0x0b, 0xf0, 0xa2, 0x20, 0x66, 0xe1, 0xf8, 0x8b, 0xb5
+       };
+
+       static unsigned char FILE_SHA256[] = {
+               0x00, 0xb8, 0x08, 0xa1, 0x60, 0x5e, 0x13, 0xfe, 0xb6, 0xc5,
+               0x71, 0x67, 0x1f, 0xb2, 0x29, 0x2b, 0xa8, 0x7f, 0x0f, 0x28,
+               0xed, 0xe3, 0xe0, 0xe3, 0x51, 0xfe, 0xd8, 0xf5, 0x7c, 0xad,
+               0x68, 0x06
+       };
+
+       struct hash_algorithm const *ha;
+       char const *name;
+       char const *input = "Fort";
+       struct rpki_uri uri = { 0 };
+
+       hash_setup();
+
+       uri.local = "resources/lorem-ipsum.txt";
+
+       ha = hash_get_sha1();
+       ck_assert_uint_eq(20, hash_get_size(ha));
+       name = hash_get_name(ha);
+       ck_assert(strcasecmp("sha1", name) || strcasecmp("sha-1", name));
+
+       ck_assert_int_eq(0, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA1, sizeof(FORT_SHA1)));
+       ck_assert_int_eq(EINVAL, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA1, sizeof(FORT_SHA1) - 1));
+       FORT_SHA1[1] = 1;
+       ck_assert_int_eq(EINVAL, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA1, sizeof(FORT_SHA1)));
+
+       ck_assert_int_eq(0, hash_validate_file(ha, &uri, FILE_SHA1, sizeof(FILE_SHA1)));
+       ck_assert_int_eq(-EINVAL, hash_validate_file(ha, &uri, FILE_SHA1, sizeof(FILE_SHA1) - 10));
+       FILE_SHA1[19] = 0;
+       ck_assert_int_eq(-EINVAL, hash_validate_file(ha, &uri, FILE_SHA1, sizeof(FILE_SHA1)));
+
+       ha = hash_get_sha256();
+       ck_assert_uint_eq(32, hash_get_size(ha));
+       name = hash_get_name(ha);
+       ck_assert(strcasecmp("sha256", name) || strcasecmp("sha-256", name));
+
+       ck_assert_int_eq(0, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA256, sizeof(FORT_SHA256)));
+       ck_assert_int_eq(EINVAL, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA256, sizeof(FORT_SHA256) - 6));
+       FORT_SHA256[10] = 0;
+       ck_assert_int_eq(EINVAL, hash_validate(ha, (unsigned char *)input, strlen(input), FORT_SHA256, sizeof(FORT_SHA256)));
+
+       ck_assert_int_eq(0, hash_validate_file(ha, &uri, FILE_SHA256, sizeof(FILE_SHA256)));
+       ck_assert_int_eq(-EINVAL, hash_validate_file(ha, &uri, FILE_SHA256, sizeof(FILE_SHA256) - 1));
+       FILE_SHA256[31] = 10;
+       ck_assert_int_eq(-EINVAL, hash_validate_file(ha, &uri, FILE_SHA256, sizeof(FILE_SHA256)));
+
+       hash_teardown();
+}
+END_TEST
+
+static Suite *
+pdu_suite(void)
+{
+       Suite *suite;
+       TCase *core;
+
+       core = tcase_create("hash");
+       tcase_add_test(core, test_hash);
+
+       suite = suite_create("hash");
+       suite_add_tcase(suite, core);
+       return suite;
+}
+
+int
+main(int argc, char **argv)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = pdu_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
index 21a8f047290a871b57ef67e83982c70c08a92419..ff0d00c0476b0c881aec0baac822e13dfa556de7 100644 (file)
@@ -15,7 +15,7 @@ START_TEST(file_line_normal)
        size_t SENTENCE_LEN;
        unsigned int i;
 
-       ck_assert_int_eq(lfile_open("line_file/core.txt", &lfile), 0);
+       ck_assert_int_eq(lfile_open("resources/line_file/core.txt", &lfile), 0);
 
        ck_assert_int_eq(lfile_read(lfile, &string), 0);
        ck_assert_str_eq(string, "This is a normal line.");
@@ -60,7 +60,7 @@ START_TEST(file_line_empty)
        struct line_file *lfile;
        char *string;
 
-       ck_assert_int_eq(lfile_open("line_file/empty.txt", &lfile), 0);
+       ck_assert_int_eq(lfile_open("resources/line_file/empty.txt", &lfile), 0);
 
        ck_assert_int_eq(lfile_read(lfile, &string), 0);
        ck_assert(string == NULL);
@@ -74,7 +74,7 @@ START_TEST(file_line_null_chara)
        struct line_file *lfile;
        char *string;
 
-       ck_assert_int_eq(lfile_open("line_file/error.txt", &lfile), 0);
+       ck_assert_int_eq(lfile_open("resources/line_file/error.txt", &lfile), 0);
 
        ck_assert_int_eq(lfile_read(lfile, &string), 0);
        ck_assert_str_eq(string, "This is a normal line.");
diff --git a/test/resources/lorem-ipsum.txt b/test/resources/lorem-ipsum.txt
new file mode 100644 (file)
index 0000000..8deed24
--- /dev/null
@@ -0,0 +1,9 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. In a malesuada neque. Nunc efficitur at leo ac feugiat. Aliquam velit erat, molestie nec nulla vitae, accumsan accumsan ipsum. Nunc mattis quam sit amet turpis sollicitudin fringilla. Sed id ante finibus, finibus erat in, vestibulum lectus. Aenean sed massa ut lacus efficitur sollicitudin. Mauris at imperdiet augue. Maecenas tempus ornare odio, egestas faucibus ante commodo id. Morbi at urna nisl. Phasellus gravida felis non erat ornare, at mattis magna venenatis. In ac lorem vel est euismod finibus. Nam mauris felis, laoreet id eros sed, suscipit gravida justo. In a dictum erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
+
+Ut sagittis nec risus vitae cursus. Mauris id ipsum sem. Sed bibendum mauris orci, eget tincidunt massa vehicula dapibus. Vivamus lobortis tellus et molestie euismod. Integer venenatis diam ut libero dapibus, in dictum tortor dictum. Nulla at ante magna. Vivamus lectus orci, efficitur a semper eget, facilisis eget orci.
+
+Maecenas venenatis lacus et est pharetra fermentum. Curabitur auctor rhoncus enim. Aliquam ut nisi suscipit, semper enim vitae, viverra quam. Aenean neque turpis, aliquam ut metus ac, tempus porta orci. Nam at nunc eu velit porta sagittis. Quisque ullamcorper diam dolor, sit amet convallis lacus dignissim et. Nullam dictum tortor lectus.
+
+Curabitur tortor sapien, auctor id laoreet nec, facilisis in mauris. Maecenas tellus nisl, sollicitudin sit amet pulvinar eu, dictum eu ligula. Vivamus vel fringilla turpis, at mattis neque. Aenean venenatis dapibus urna ac pulvinar. Cras ut ante a ipsum volutpat pellentesque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla facilisi. Aliquam ultrices nibh vel turpis laoreet tristique.
+
+Aliquam in orci sed lectus dapibus mollis sit amet nec ligula. Aliquam nec neque enim. Ut rutrum pulvinar urna a gravida. Ut vulputate tortor vitae risus molestie, quis accumsan sapien elementum. Nulla facilisi. Suspendisse potenti. Nam lacus tortor, condimentum eget eleifend sed, fermentum a lorem. Mauris sit amet quam at eros maximus eleifend vel quis metus. Fusce molestie feugiat orci. Morbi aliquet nec diam vel fermentum. Nullam fermentum eget odio eu dapibus. Praesent varius aliquet ante sit amet vehicula. Aliquam sed enim ut dui malesuada pellentesque eget finibus urna.
index 18ef83681acf07474d59129fa326b53730a351d4..6e292cb84b3068d891350066563ca0712b7e23e4 100644 (file)
@@ -22,8 +22,8 @@ MOCK_ABORT_INT(delete_dir_recursive_bottom_up, char const *path)
 MOCK_ABORT_INT(mkdir_p, char const *path, bool include_basename)
 MOCK_ABORT_VOID(fnstack_pop, void)
 MOCK_ABORT_VOID(fnstack_push_uri, struct rpki_uri *uri)
-MOCK_ABORT_INT(hash_validate_file, struct rpki_uri *uri,
-    unsigned char const *expected, size_t expected_len)
+MOCK_ABORT_INT(hash_validate_file, struct hash_algorithm const *algorithm,
+    struct rpki_uri *uri, unsigned char const *expected, size_t expected_len)
 MOCK_ABORT_INT(relax_ng_parse, const char *path, xml_read_cb cb, void *arg)
 MOCK_ABORT_PTR(state_retrieve, validation, void)
 __MOCK_ABORT(tal_get_file_name, char const *, NULL, struct tal *tal)