diff --git a/DESCRIPTION b/DESCRIPTION index 661295f..9446a97 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: secretbase Type: Package Title: Cryptographic Hash and Extendable-Output Functions -Version: 0.3.0.9006 +Version: 0.3.0.9007 Description: Fast and memory-efficient streaming hash functions. Performs direct hashing of strings, raw bytes, and files potentially larger than memory, as well as hashing in-memory objects through R's serialization mechanism, diff --git a/NAMESPACE b/NAMESPACE index 0be9c2c..586a474 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,4 +3,5 @@ export(sha256) export(sha3) export(siphash13) +export(siphash24) useDynLib(secretbase, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md index a28df80..3d61ab0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ -# secretbase 0.3.0.9006 (development) +# secretbase 0.3.0.9007 (development) * Adds HMAC generation to `sha256()`. -* Adds SipHash-1-3 pseudo-random function (PRF) as a fast, cryptographically-strong keyed hash. +* Adds SipHash pseudo-random function (PRF) as a fast, cryptographically-strong keyed hash. # secretbase 0.3.0.1 diff --git a/R/base.R b/R/base.R index 566c63c..931409a 100644 --- a/R/base.R +++ b/R/base.R @@ -35,9 +35,9 @@ # secretbase - Main Functions -------------------------------------------------- -#' Cryptographic Hashing Using the SHA-3 Algorithms +#' SHA-3 Cryptographic Hash Algorithms and SHAKE256 XOF #' -#' Returns a SHA-3 hash of the supplied object or file. +#' Returns a SHA-3 or SHAKE256 hash of the supplied object or file. #' #' @param x object to hash. A character string or raw vector (without #' attributes) is hashed 'as is'. All other objects are stream hashed using @@ -99,9 +99,9 @@ sha3 <- function(x, bits = 256L, convert = TRUE, file) if (missing(file)) .Call(secretbase_sha3, x, bits, convert) else .Call(secretbase_sha3_file, file, bits, convert) -#' Cryptographic Hashing Using the SHA-256 Algorithm +#' SHA-256 Cryptographic Hash Algorithm #' -#' Returns a SHA-256 hash of the supplied object or file, or an HMAC if a secret +#' Returns a SHA-256 hash of the supplied object or file, or HMAC if a secret #' key is supplied. #' #' @inheritParams sha3 @@ -144,10 +144,12 @@ sha256 <- function(x, key = NULL, convert = TRUE, file) if (missing(file)) .Call(secretbase_sha256, x, key, convert) else .Call(secretbase_sha256_file, file, key, convert) -#' Hashing Using the SipHash-1-3 Pseudorandom Function +#' SipHash Pseudorandom Function #' -#' Returns a fast, cryptographically-strong SipHash-1-3 keyed hash of the -#' supplied object or file. +#' Returns a fast, cryptographically-strong SipHash keyed hash of the supplied +#' object or file. SipHash-1-3 is optimised for performance, whereas +#' SipHash-2-4 is recommended for security. Note: SipHash is not a +#' cryptographic hash algorithm. #' #' @inheritParams sha3 #' @param key [default NULL] a character string or raw vector comprising the 16 @@ -177,11 +179,11 @@ sha256 <- function(x, key = NULL, convert = TRUE, file) #' # SipHash-1-3 hash as raw vector: #' siphash13("secret base", convert = FALSE) #' -#' # SipHash-1-3 hash using a character string key: -#' siphash13("secret", key = "base") +#' # SipHash-2-4 hash using a character string key: +#' siphash24("secret", key = "base") #' -#' # SipHash-1-3 hash using a raw vector key: -#' siphash13("secret", key = charToRaw("base")) +#' # SipHash-2-4 hash using a raw vector key: +#' siphash24("secret", key = charToRaw("base")) #' #' # SipHash-1-3 hash a file: #' file <- tempfile(); cat("secret base", file = file) @@ -193,3 +195,10 @@ sha256 <- function(x, key = NULL, convert = TRUE, file) siphash13 <- function(x, key = NULL, convert = TRUE, file) if (missing(file)) .Call(secretbase_siphash13, x, key, convert) else .Call(secretbase_siphash13_file, file, key, convert) + +#' @rdname siphash13 +#' @export +#' +siphash24 <- function(x, key = NULL, convert = TRUE, file) + if (missing(file)) .Call(secretbase_siphash24, x, key, convert) else + .Call(secretbase_siphash24_file, file, key, convert) diff --git a/README.Rmd b/README.Rmd index 87d542c..4613d66 100644 --- a/README.Rmd +++ b/README.Rmd @@ -29,7 +29,7 @@ Implementations include the SHA-256 and SHA-3 cryptographic hash functions, SHAK The SHA-3 Secure Hash Standard was published by the National Institute of Standards and Technology (NIST) in 2015 at [doi:10.6028/NIST.FIPS.202](https://dx.doi.org/10.6028/NIST.FIPS.202). The SHA-256 Secure Hash Standard was published by NIST in 2002 at . The SipHash family of pseudo-random functions by Jean-Philippe Aumasson and Daniel J. Bernstein was published in 2012 at .[1] -The SHA-256 and SHA-3 implementations are based on those by the 'Mbed TLS' Trusted Firmware Project at . The SipHash-1-3 implementation is based on that of Daniele Nicolodi, David Rheinsberg and Tom Gundersen at , which is in turn based on the reference implementation by Jean-Philippe Aumasson and Daniel J. Bernstein released to the public domain at . +The SHA-256 and SHA-3 implementations are based on those by the 'Mbed TLS' Trusted Firmware Project at . The SipHash implementation is based on that of Daniele Nicolodi, David Rheinsberg and Tom Gundersen at , which is in turn based on the reference implementation by Jean-Philippe Aumasson and Daniel J. Bernstein released to the public domain at . ### Installation @@ -47,9 +47,7 @@ install.packages("secretbase", repos = "https://shikokuchuo.r-universe.dev") ### Quick Start -`secretbase` offers the functions: `sha3()`, `sha256()` and `siphash13()`. - -##### SHA-3 and XOF usage: +#### SHA-3 and XOF usage: - For the SHA-3 cryptographic hash algorithm, specify 'bits' as `224`, `256`, `384` or `512` - For the SHAKE256 extendable-output function (XOF), specify any other bit length @@ -65,7 +63,7 @@ sha3("秘密の基地の中", bits = 512) ``` -##### Hash arbitrary R objects: +#### Hash arbitrary R objects: - Uses memory-efficient 'streaming' serialization, without allocation of the serialized object - Portable as always uses R serialization version 3 big-endian representation, skipping headers (which contain R version and native encoding information) @@ -76,7 +74,7 @@ sha3(data.frame(a = 1, b = 2), bits = 160) sha3(NULL) ``` -##### Hash files: +#### Hash files: - Performed in a streaming fashion, accepting files larger than memory @@ -88,7 +86,7 @@ sha3(file = file) unlink(file) ``` -##### Hash to integer: +#### Hash to integer: - Specify 'convert' as `NA` (and 'bits' as `32` for a single integer value) - May be supplied as deterministic random seeds for R's pseudo random number generators (RNGs) @@ -101,7 +99,7 @@ sha3("秘密の基地の中", bits = 32, convert = NA) For use in parallel computing, this is a valid method for reducing to a negligible probability that RNGs in each process may overlap. This may be especially suitable when first-best alternatives such as using recursive streams are too expensive or unable to preserve reproducibility. [2] -##### Generating a SHA-256 HMAC: +#### Generating a SHA-256 HMAC: - Use `sha256()` passing a character string or raw vector to 'key'. @@ -109,13 +107,16 @@ For use in parallel computing, this is a valid method for reducing to a negligib sha256("secret base", key = "秘密の基地の中") ``` -##### Using SipHash: +#### Using SipHash: -- SipHash is a fast, cryptographically-strong keyed hash. The SipHash-1-3 parameters are optimized for performance. +- SipHash is a fast, cryptographically-strong keyed hash. - Pass a character string or raw vector to 'key'. Up to 16 bytes (128 bits) of the key data is used. +- SipHash-1-3 is optimized for performance; SipHash-2-4 recommended for security. ```{r siphash} siphash13("secret base", key = charToRaw("秘密の基地の中")) + +siphash24("secret base", key = charToRaw("秘密の基地の中")) ``` ### References diff --git a/README.md b/README.md index 1b46481..0f23bbf 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Daniel J. Bernstein was published in 2012 at The SHA-256 and SHA-3 implementations are based on those by the ‘Mbed TLS’ Trusted Firmware Project at -. The SipHash-1-3 +. The SipHash implementation is based on that of Daniele Nicolodi, David Rheinsberg and Tom Gundersen at , which is in turn based on the reference implementation by Jean-Philippe Aumasson and @@ -57,10 +57,7 @@ install.packages("secretbase", repos = "https://shikokuchuo.r-universe.dev") ### Quick Start -`secretbase` offers the functions: `sha3()`, `sha256()` and -`siphash13()`. - -##### SHA-3 and XOF usage: +#### SHA-3 and XOF usage: - For the SHA-3 cryptographic hash algorithm, specify ‘bits’ as `224`, `256`, `384` or `512` @@ -81,7 +78,7 @@ sha3("秘密の基地の中", bits = 512) #> [1] "e30cdc73f6575c40d55b5edc8eb4f97940f5ca491640b41612e02a05f3e59dd9c6c33f601d8d7a8e2ca0504b8c22f7bc69fa8f10d7c01aab392781ff4ae1e610" ``` -##### Hash arbitrary R objects: +#### Hash arbitrary R objects: - Uses memory-efficient ‘streaming’ serialization, without allocation of the serialized object @@ -97,7 +94,7 @@ sha3(NULL) #> [1] "b3e37e4c5def1bfb2841b79ef8503b83d1fed46836b5b913d7c16de92966dcee" ``` -##### Hash files: +#### Hash files: - Performed in a streaming fashion, accepting files larger than memory @@ -107,7 +104,7 @@ sha3(file = file) #> [1] "a721d57570e7ce366adee2fccbe9770723c6e3622549c31c7cab9dbb4a795520" ``` -##### Hash to integer: +#### Hash to integer: - Specify ‘convert’ as `NA` (and ‘bits’ as `32` for a single integer value) @@ -129,7 +126,7 @@ be especially suitable when first-best alternatives such as using recursive streams are too expensive or unable to preserve reproducibility. \[2\] -##### Generating a SHA-256 HMAC: +#### Generating a SHA-256 HMAC: - Use `sha256()` passing a character string or raw vector to ‘key’. @@ -138,16 +135,20 @@ sha256("secret base", key = "秘密の基地の中") #> [1] "ec58099ab21325e792bef8f1aafc0a70e1a7227463cfc410931112705d753392" ``` -##### Using SipHash: +#### Using SipHash: -- SipHash is a fast, cryptographically-strong keyed hash. The - SipHash-1-3 parameters are optimized for performance. +- SipHash is a fast, cryptographically-strong keyed hash. - Pass a character string or raw vector to ‘key’. Up to 16 bytes (128 bits) of the key data is used. +- SipHash-1-3 is optimized for performance; SipHash-2-4 recommended for + security. ``` r siphash13("secret base", key = charToRaw("秘密の基地の中")) #> [1] "a1f0a751892cc7dd" + +siphash24("secret base", key = charToRaw("秘密の基地の中")) +#> [1] "1bedfe817cac0562" ``` ### References diff --git a/man/sha256.Rd b/man/sha256.Rd index 7388966..bfa3af8 100644 --- a/man/sha256.Rd +++ b/man/sha256.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/base.R \name{sha256} \alias{sha256} -\title{Cryptographic Hashing Using the SHA-256 Algorithm} +\title{SHA-256 Cryptographic Hash Algorithm} \usage{ sha256(x, key = NULL, convert = TRUE, file) } @@ -30,7 +30,7 @@ file is stream hashed, thus capable of handling files larger than memory.} A character string, raw or integer vector depending on 'convert'. } \description{ -Returns a SHA-256 hash of the supplied object or file, or an HMAC if a secret +Returns a SHA-256 hash of the supplied object or file, or HMAC if a secret key is supplied. } \details{ diff --git a/man/sha3.Rd b/man/sha3.Rd index 2877832..6c1b637 100644 --- a/man/sha3.Rd +++ b/man/sha3.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/base.R \name{sha3} \alias{sha3} -\title{Cryptographic Hashing Using the SHA-3 Algorithms} +\title{SHA-3 Cryptographic Hash Algorithms and SHAKE256 XOF} \usage{ sha3(x, bits = 256L, convert = TRUE, file) } @@ -30,7 +30,7 @@ file is stream hashed, thus capable of handling files larger than memory.} A character string, raw or integer vector depending on 'convert'. } \description{ -Returns a SHA-3 hash of the supplied object or file. +Returns a SHA-3 or SHAKE256 hash of the supplied object or file. } \details{ To produce single integer values suitable for use as random seeds diff --git a/man/siphash13.Rd b/man/siphash13.Rd index 31a2725..9899b51 100644 --- a/man/siphash13.Rd +++ b/man/siphash13.Rd @@ -2,9 +2,12 @@ % Please edit documentation in R/base.R \name{siphash13} \alias{siphash13} -\title{Hashing Using the SipHash-1-3 Pseudorandom Function} +\alias{siphash24} +\title{SipHash Pseudorandom Function} \usage{ siphash13(x, key = NULL, convert = TRUE, file) + +siphash24(x, key = NULL, convert = TRUE, file) } \arguments{ \item{x}{object to hash. A character string or raw vector (without @@ -31,8 +34,10 @@ file is stream hashed, thus capable of handling files larger than memory.} A character string, raw or integer vector depending on 'convert'. } \description{ -Returns a fast, cryptographically-strong SipHash-1-3 keyed hash of the - supplied object or file. +Returns a fast, cryptographically-strong SipHash keyed hash of the supplied + object or file. SipHash-1-3 is optimised for performance, whereas + SipHash-2-4 is recommended for security. Note: SipHash is not a + cryptographic hash algorithm. } \details{ The SipHash family of cryptographically-strong pseudorandom @@ -54,11 +59,11 @@ siphash13("secret base") # SipHash-1-3 hash as raw vector: siphash13("secret base", convert = FALSE) -# SipHash-1-3 hash using a character string key: -siphash13("secret", key = "base") +# SipHash-2-4 hash using a character string key: +siphash24("secret", key = "base") -# SipHash-1-3 hash using a raw vector key: -siphash13("secret", key = charToRaw("base")) +# SipHash-2-4 hash using a raw vector key: +siphash24("secret", key = charToRaw("base")) # SipHash-1-3 hash a file: file <- tempfile(); cat("secret base", file = file) diff --git a/src/init.c b/src/init.c index c5031ad..412f5eb 100644 --- a/src/init.c +++ b/src/init.c @@ -25,6 +25,8 @@ static const R_CallMethodDef callMethods[] = { {"secretbase_sha256_file", (DL_FUNC) &secretbase_sha256_file, 3}, {"secretbase_siphash13", (DL_FUNC) &secretbase_siphash13, 3}, {"secretbase_siphash13_file", (DL_FUNC) &secretbase_siphash13_file, 3}, + {"secretbase_siphash24", (DL_FUNC) &secretbase_siphash24, 3}, + {"secretbase_siphash24_file", (DL_FUNC) &secretbase_siphash24_file, 3}, {NULL, NULL, 0} }; diff --git a/src/secret.h b/src/secret.h index 68cdf76..a865eda 100644 --- a/src/secret.h +++ b/src/secret.h @@ -93,6 +93,7 @@ typedef struct secretbase_sha256_context { typedef struct secretbase_siphash_context { int skip; + unsigned N; CSipHash *ctx; } secretbase_siphash_context; @@ -105,5 +106,7 @@ SEXP secretbase_sha256(SEXP, SEXP, SEXP); SEXP secretbase_sha256_file(SEXP, SEXP, SEXP); SEXP secretbase_siphash13(SEXP, SEXP, SEXP); SEXP secretbase_siphash13_file(SEXP, SEXP, SEXP); +SEXP secretbase_siphash24(SEXP, SEXP, SEXP); +SEXP secretbase_siphash24_file(SEXP, SEXP, SEXP); #endif diff --git a/src/secret3.c b/src/secret3.c index 7132bdc..758c4d4 100644 --- a/src/secret3.c +++ b/src/secret3.c @@ -18,7 +18,7 @@ #include "secret.h" -// secretbase - SipHash-1-3 implementation ------------------------------------- +// secretbase - SipHash implementation ----------------------------------------- // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published @@ -183,24 +183,16 @@ static inline uint64_t c_siphash_finalize_NM(CSipHash *state, unsigned N, unsign } -static void c_siphash_append_13(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { - c_siphash_append_N(state, bytes, n_bytes, 1); -} - -static uint64_t c_siphash_finalize_13(CSipHash *state) { - return c_siphash_finalize_NM(state, 1, 3); -} - // secretbase - internals ------------------------------------------------------ static void hash_bytes(R_outpstream_t stream, void *src, int len) { secretbase_siphash_context *sctx = (secretbase_siphash_context *) stream->data; - sctx->skip ? (void) sctx->skip-- : c_siphash_append_13(sctx->ctx, (uint8_t *) src, (size_t) len); + sctx->skip ? (void) sctx->skip-- : c_siphash_append_N(sctx->ctx, (uint8_t *) src, (size_t) len, sctx->N); } -static void hash_file(CSipHash *ctx, const SEXP x) { +static void hash_file(CSipHash *ctx, const SEXP x, const unsigned N) { if (TYPEOF(x) != STRSXP) Rf_error("'file' must be specified as a character string"); @@ -215,7 +207,7 @@ static void hash_file(CSipHash *ctx, const SEXP x) { setbuf(f, NULL); while ((cur = fread(buf, sizeof(char), SB_BUF_SIZE, f))) { - c_siphash_append_13(ctx, buf, cur); + c_siphash_append_N(ctx, buf, cur, N); } if (ferror(f)) { @@ -226,19 +218,19 @@ static void hash_file(CSipHash *ctx, const SEXP x) { } -static void hash_object(CSipHash *ctx, const SEXP x) { +static void hash_object(CSipHash *ctx, const SEXP x, const unsigned N) { switch (TYPEOF(x)) { case STRSXP: if (XLENGTH(x) == 1 && ATTRIB(x) == R_NilValue) { const char *s = CHAR(STRING_ELT(x, 0)); - c_siphash_append_13(ctx, (uint8_t *) s, strlen(s)); + c_siphash_append_N(ctx, (uint8_t *) s, strlen(s), N); return; } break; case RAWSXP: if (ATTRIB(x) == R_NilValue) { - c_siphash_append_13(ctx, (uint8_t *) STDVEC_DATAPTR(x), (size_t) XLENGTH(x)); + c_siphash_append_N(ctx, (uint8_t *) STDVEC_DATAPTR(x), (size_t) XLENGTH(x), N); return; } break; @@ -246,6 +238,7 @@ static void hash_object(CSipHash *ctx, const SEXP x) { secretbase_siphash_context sctx; sctx.skip = SB_SERIAL_HEADERS; + sctx.N = N; sctx.ctx = ctx; struct R_outpstream_st output_stream; @@ -263,8 +256,9 @@ static void hash_object(CSipHash *ctx, const SEXP x) { } -static SEXP secretbase_siphash13_impl(const SEXP x, const SEXP key, const SEXP convert, - void (*const hash_func)(CSipHash *, SEXP)) { +static SEXP secretbase_siphash_impl(const SEXP x, const SEXP key, const SEXP convert, + void (*const hash_func)(CSipHash *, SEXP, unsigned), + const unsigned N, const unsigned M) { const int conv = LOGICAL(convert)[0]; uint64_t hash; @@ -292,8 +286,8 @@ static SEXP secretbase_siphash13_impl(const SEXP x, const SEXP key, const SEXP c memcpy(seed, data, klen < SB_SKEY_SIZE ? klen : SB_SKEY_SIZE); c_siphash_init(&ctx, seed); } - hash_func(&ctx, x); - hash = c_siphash_finalize_13(&ctx); + hash_func(&ctx, x, N); + hash = c_siphash_finalize_NM(&ctx, N, M); clear_buffer(&ctx, sizeof(CSipHash)); return hash_to_sexp((unsigned char *) &hash, SB_SIPH_SIZE, conv); @@ -304,12 +298,24 @@ static SEXP secretbase_siphash13_impl(const SEXP x, const SEXP key, const SEXP c SEXP secretbase_siphash13(SEXP x, SEXP key, SEXP convert) { - return secretbase_siphash13_impl(x, key, convert, hash_object); + return secretbase_siphash_impl(x, key, convert, hash_object, 1u, 3u); } SEXP secretbase_siphash13_file(SEXP x, SEXP key, SEXP convert) { - return secretbase_siphash13_impl(x, key, convert, hash_file); + return secretbase_siphash_impl(x, key, convert, hash_file, 1u, 3u); + +} + +SEXP secretbase_siphash24(SEXP x, SEXP key, SEXP convert) { + + return secretbase_siphash_impl(x, key, convert, hash_object, 2u, 4u); + +} + +SEXP secretbase_siphash24_file(SEXP x, SEXP key, SEXP convert) { + + return secretbase_siphash_impl(x, key, convert, hash_file, 2u, 4u); } diff --git a/tests/tests.R b/tests/tests.R index e45af15..7b821ff 100644 --- a/tests/tests.R +++ b/tests/tests.R @@ -68,11 +68,13 @@ test_equal(sha256("secret base", key = as.raw(1L)), "35a0fc031777e1a16b2c11a6145 test_equal(sha256("secret base", key = rep(c(as.raw(1L), as.raw(2L)), 64L)), "0d9cbfe4872e0d9ef16f86fbbe5397fd4ed30b7e50b4c5c7722ccf4786aa58d2") test_equal(sha256("secret base", key = character()), "6bc4693e2025baadf345dd0b133b867ac081dbf6ae02e94e774db4b1a65203ca") test_error(sha256("secret base", key = list()), "'key' must be a character string, raw vector or NULL") -# SipHash13 tests: +# SipHash tests: test_equal(siphash13(""), "2c530c1562a7fbd1") +test_equal(siphash24(""), "d70077739d4b921e") test_equal(siphash13("", key = ""), "2c530c1562a7fbd1") test_equal(siphash13("", key = character()), "2c530c1562a7fbd1") test_equal(siphash13("secret base"), "48c60a316babef0e") +test_equal(siphash24("secret base"), "bdb6899a934fdf5a") test_equal(siphash13("secret base", key = "secret base"), "2cf27a8f22f02e59") test_equal(siphash13("secret base", key = c("secret base", "more")), "2cf27a8f22f02e59") test_equal(siphash13("secret base", key = as.raw(1L)), "5ecd894f7d269521") @@ -86,12 +88,14 @@ test_equal(siphash13(NULL), "08d5f59e833de599") test_equal(siphash13(substitute()), "c8cadc1ab377142a") test_equal(siphash13(`class<-`(siphash13(character(), convert = FALSE), "hash")), "39124d8b9643418a") test_error(siphash13(file = NULL), "'file' must be specified as a character string") -hash_func <- function(file, string) { +hash_func <- function(file, string, func) { on.exit(unlink(file)) cat(string, file = file) - siphash13(file = file) + func(file = file) } -test_equal(hash_func(tempfile(), "secret base"), "48c60a316babef0e") -test_error(hash_func("", ""), "file not found or no read permission") +test_equal(hash_func(tempfile(), "secret base", siphash13), "48c60a316babef0e") +test_equal(hash_func(tempfile(), "secret base", siphash24), "bdb6899a934fdf5a") +test_error(hash_func("", "", siphash13), "file not found or no read permission") +test_error(hash_func("", "", siphash24), "file not found or no read permission") if (.Platform[["OS.type"]] == "unix") test_error(siphash13(file = "~/"), "file read error") test_equal(siphash13(paste(1:888, collapse = "")), "8337f50b05209c40")