diff --git a/DESCRIPTION b/DESCRIPTION index fc9163c..8b86557 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,12 +1,12 @@ Package: secretbase Type: Package Title: Cryptographic Hash and Extendable-Output Functions -Version: 0.2.0 -Description: SHA-3 cryptographic hash and SHAKE256 extendable-output functions - (XOF). The SHA-3 Secure Hash Standard was published by the National - Institute of Standards and Technology (NIST) in 2015 at +Version: 0.2.0.9000 +Description: SHA-256, SHA-3 cryptographic hash and SHAKE256 extendable-output + functions (XOF). The SHA-3 Secure Hash Standard was published by the + National Institute of Standards and Technology (NIST) in 2015 at . Fast and memory-efficient implementation using - the core algorithm from 'Mbed TLS' under the Trusted Firmware Project + the core algorithms from 'Mbed TLS' under the Trusted Firmware Project . Authors@R: c(person(given = "Charlie", diff --git a/NAMESPACE b/NAMESPACE index 4caf612..25484f0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,5 @@ # Generated by roxygen2: do not edit by hand +export(sha256) export(sha3) -export(sha3file) useDynLib(secretbase, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md index 8601689..7c4dab7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# secretbase 0.2.0.9000 (development) + +* Adds SHA-256 cryptographic hash algorithm. +* Folds file hashing into the 'file' argument of the main hash function. + # secretbase 0.2.0 * Adds file hashing interface. diff --git a/R/base.R b/R/base.R index d68e823..52ed8b6 100644 --- a/R/base.R +++ b/R/base.R @@ -18,10 +18,10 @@ #' secretbase: Cryptographic Hash and Extendable-Output Functions #' -#' SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). Fast -#' and memory-efficient implementation using the core algorithm from 'Mbed -#' TLS' under the Trusted Firmware Project -#' . The SHA-3 +#' SHA-256, SHA-3 cryptographic hash and SHAKE256 extendable-output functions +#' (XOF). Fast and memory-efficient implementation using the core algorithms +#' from 'Mbed TLS' under the Trusted Firmware Project +#' \url{https://www.trustedfirmware.org/projects/mbed-tls/}.\cr\cr The SHA-3 #' cryptographic hash functions are SHA3-224, SHA3-256, SHA3-384 and #' SHA3-512, each an instance of the Keccak algorithm. SHAKE256 is one of #' the two XOFs of the SHA-3 family, along with SHAKE128 (not implemented). @@ -29,6 +29,9 @@ #' @references The SHA-3 Secure Hash Standard was published by the National #' Institute of Standards and Technology (NIST) in 2015 at #' \doi{doi:10.6028/NIST.FIPS.202}. +#' +#' The SHA-256 Secure Hash Standard was published by NIST in 2002 at +#' \url{https://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf}. #' #' @encoding UTF-8 #' @author Charlie Gao \email{charlie.gao@@shikokuchuo.net} @@ -40,7 +43,7 @@ # secretbase - Main Functions -------------------------------------------------- -#' Cryptographic Hashing Using the SHA-3 Algorithm +#' Cryptographic Hashing Using the SHA-3 Algorithms #' #' Returns a SHA-3 hash of the supplied R object or file. #' @@ -57,6 +60,8 @@ #' @param convert [default TRUE] if TRUE, the hash is converted to its hex #' representation as a character string, if FALSE, output directly as a raw #' vector, or if NA, a vector of (32-bit) integer values. +#' @param file character file name / path. If specified, 'x' is ignored. The +#' file is hashed in a streaming fashion and may be larger than memory. #' #' @return A character string, raw or integer vector depending on 'convert'. #' @@ -83,22 +88,39 @@ #' # SHAKE256 hash to integer: #' sha3("secret base", bits = 32L, convert = NA) #' +#' # SHA3-256 hash a file: +#' file <- tempfile(); cat("secret base", file = file) +#' sha3(file = file) +#' unlink(file) +#' #' @export #' -sha3 <- function(x, bits = 256L, convert = TRUE) - .Call(secretbase_sha3, x, bits, convert) +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) -#' @param file character file name / path. The file is hashed in a streaming -#' fashion and does not need to fit in memory. -#' +#' Cryptographic Hashing Using the SHA-256 Algorithm +#' +#' Returns a SHA-256 hash of the supplied R object or file. +#' +#' @inheritParams sha3 +#' +#' @return A character string, raw or integer vector depending on 'convert'. +#' #' @examples -#' # SHA3-256 hash a file: +#' # SHA-256 hash as character string: +#' sha256("secret base") +#' +#' # SHA-256 hash as raw vector: +#' sha256("secret base", convert = FALSE) +#' +#' # SHA-256 hash a file: #' file <- tempfile(); cat("secret base", file = file) -#' sha3file(file) +#' sha256(file = file) #' unlink(file) -#' -#' @rdname sha3 +#' #' @export #' -sha3file <- function(file, bits = 256L, convert = TRUE) - .Call(secretbase_sha3_file, file, bits, convert) +sha256 <- function(x, convert = TRUE, file) + if (missing(file)) .Call(secretbase_sha256, x, convert) else + .Call(secretbase_sha256_file, file, convert) diff --git a/README.Rmd b/README.Rmd index 0e6c5fe..7d427b4 100644 --- a/README.Rmd +++ b/README.Rmd @@ -23,11 +23,11 @@ knitr::opts_chunk$set( [![DOI](https://zenodo.org/badge/745691432.svg)](https://zenodo.org/doi/10.5281/zenodo.10553139) -SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). +SHA-256, SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). 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). -Fast and memory-efficient implementation using the core algorithm from 'Mbed TLS' under the Trusted Firmware Project . +Fast and memory-efficient implementation using the core algorithms from 'Mbed TLS' under the Trusted Firmware Project . ### Installation @@ -45,12 +45,12 @@ install.packages("secretbase", repos = "https://shikokuchuo.r-universe.dev") ### Quick Start -`secretbase` offers the functions: `sha3()` for objects and `sha3file()` for files. +`secretbase` offers the functions: `sha3()` and `sha256()`. -To use: +For `sha3()`, to use: - - SHA-3 cryptographic hash algorithm, specify 'bits' as one of `224`, `256`, `384` or `512` - - SHAKE256 extendable-output function (XOF), specify any other arbitrary bit length + - SHA-3 cryptographic hash algorithm, specify 'bits' as `224`, `256`, `384` or `512` + - SHAKE256 extendable-output function (XOF), specify any other bit length ```{r secretbase} library(secretbase) @@ -59,16 +59,14 @@ sha3("secret base") sha3("secret base", convert = FALSE) -sha3("秘密の基地の中", bits = 224) - -sha3("", bits = 512) +sha3("秘密の基地の中", bits = 512) ``` Hash arbitrary R objects: - - using R serialization in a memory-efficient 'streaming' manner without allocation of the serialized object - - ensures portability by always using serialization v3 XDR, skipping the headers (which contain R version and encoding information) + - uses memory-efficient 'streaming' serialization (no allocation of serialized object) + - portable as always uses R serialization v3 XDR, skipping headers (containing R version and encoding information) ```{r streaming} sha3(data.frame(a = 1, b = 2), bits = 160) @@ -78,11 +76,11 @@ sha3(NULL) Hash files: - - read in a streaming fashion so can be larger than memory + - in a streaming fashion, accepting files larger than memory ```{r files} file <- tempfile(); cat("secret base", file = file) -sha3file(file) +sha3(file = file) ``` ```{r unlink, echo=FALSE} unlink(file) @@ -90,8 +88,8 @@ unlink(file) Hash to integer: - - specify 'convert' as `NA` - - specify 'bits' as `32` for a single integer value + - 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) ```{r integer} sha3("秘密の基地の中", bits = 384, convert = NA) @@ -99,8 +97,6 @@ sha3("秘密の基地の中", bits = 384, convert = NA) sha3("秘密の基地の中", bits = 32, convert = NA) ``` -These values may be supplied as deterministic (but indistinguishable from random) seeds for R's pseudo random number generators (RNGs). - 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. [1] ### References diff --git a/README.md b/README.md index b0d1918..933e51d 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,14 @@ badge](https://shikokuchuo.r-universe.dev/badges/secretbase?color=e4723a)](https [![DOI](https://zenodo.org/badge/745691432.svg)](https://zenodo.org/doi/10.5281/zenodo.10553139) -SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). +SHA-256, SHA-3 cryptographic hash and SHAKE256 extendable-output +functions (XOF). 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). -Fast and memory-efficient implementation using the core algorithm from +Fast and memory-efficient implementation using the core algorithms from ‘Mbed TLS’ under the Trusted Firmware Project . @@ -40,15 +41,14 @@ install.packages("secretbase", repos = "https://shikokuchuo.r-universe.dev") ### Quick Start -`secretbase` offers the functions: `sha3()` for objects and `sha3file()` -for files. +`secretbase` offers the functions: `sha3()` and `sha256()`. -To use: +For `sha3()`, to use: -- SHA-3 cryptographic hash algorithm, specify ‘bits’ as one of `224`, - `256`, `384` or `512` -- SHAKE256 extendable-output function (XOF), specify any other arbitrary - bit length +- SHA-3 cryptographic hash algorithm, specify ‘bits’ as `224`, `256`, + `384` or `512` +- SHAKE256 extendable-output function (XOF), specify any other bit + length ``` r library(secretbase) @@ -60,19 +60,16 @@ sha3("secret base", convert = FALSE) #> [1] a7 21 d5 75 70 e7 ce 36 6a de e2 fc cb e9 77 07 23 c6 e3 62 25 49 c3 1c 7c #> [26] ab 9d bb 4a 79 55 20 -sha3("秘密の基地の中", bits = 224) -#> [1] "d9e291d0c9f3dc3007dc0c111aea0b6a938929c8b4766332d8ea791a" - -sha3("", bits = 512) -#> [1] "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26" +sha3("秘密の基地の中", bits = 512) +#> [1] "e30cdc73f6575c40d55b5edc8eb4f97940f5ca491640b41612e02a05f3e59dd9c6c33f601d8d7a8e2ca0504b8c22f7bc69fa8f10d7c01aab392781ff4ae1e610" ``` Hash arbitrary R objects: -- using R serialization in a memory-efficient ‘streaming’ manner without - allocation of the serialized object -- ensures portability by always using serialization v3 XDR, skipping the - headers (which contain R version and encoding information) +- uses memory-efficient ‘streaming’ serialization (no allocation of + serialized object) +- portable as always uses R serialization v3 XDR, skipping headers + (containing R version and encoding information) ``` r sha3(data.frame(a = 1, b = 2), bits = 160) @@ -84,18 +81,20 @@ sha3(NULL) Hash files: -- read in a streaming fashion so can be larger than memory +- in a streaming fashion, accepting files larger than memory ``` r file <- tempfile(); cat("secret base", file = file) -sha3file(file) +sha3(file = file) #> [1] "a721d57570e7ce366adee2fccbe9770723c6e3622549c31c7cab9dbb4a795520" ``` Hash to integer: -- specify ‘convert’ as `NA` -- specify ‘bits’ as `32` for a single integer value +- 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) ``` r sha3("秘密の基地の中", bits = 384, convert = NA) @@ -106,9 +105,6 @@ sha3("秘密の基地の中", bits = 32, convert = NA) #> [1] 2000208511 ``` -These values may be supplied as deterministic (but indistinguishable -from random) seeds for R’s pseudo random number generators (RNGs). - 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 diff --git a/man/secretbase-package.Rd b/man/secretbase-package.Rd index 6e76f82..8162220 100644 --- a/man/secretbase-package.Rd +++ b/man/secretbase-package.Rd @@ -7,10 +7,10 @@ \alias{secretbase-package} \title{secretbase: Cryptographic Hash and Extendable-Output Functions} \description{ -SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). Fast - and memory-efficient implementation using the core algorithm from 'Mbed - TLS' under the Trusted Firmware Project - . The SHA-3 +SHA-256, SHA-3 cryptographic hash and SHAKE256 extendable-output functions + (XOF). Fast and memory-efficient implementation using the core algorithms + from 'Mbed TLS' under the Trusted Firmware Project + \url{https://www.trustedfirmware.org/projects/mbed-tls/}.\cr\cr The SHA-3 cryptographic hash functions are SHA3-224, SHA3-256, SHA3-384 and SHA3-512, each an instance of the Keccak algorithm. SHAKE256 is one of the two XOFs of the SHA-3 family, along with SHAKE128 (not implemented). @@ -19,6 +19,9 @@ SHA-3 cryptographic hash and SHAKE256 extendable-output functions (XOF). Fast The SHA-3 Secure Hash Standard was published by the National Institute of Standards and Technology (NIST) in 2015 at \doi{doi:10.6028/NIST.FIPS.202}. + + The SHA-256 Secure Hash Standard was published by NIST in 2002 at + \url{https://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf}. } \seealso{ Useful links: diff --git a/man/sha256.Rd b/man/sha256.Rd new file mode 100644 index 0000000..32b2728 --- /dev/null +++ b/man/sha256.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/base.R +\name{sha256} +\alias{sha256} +\title{Cryptographic Hashing Using the SHA-256 Algorithm} +\usage{ +sha256(x, convert = TRUE, file) +} +\arguments{ +\item{x}{R object to hash. A character string or raw vector (without +attributes) is hashed 'as is'. All other objects are hashed using R +serialization in a memory-efficient 'streaming' manner, without +allocation of the serialized object. To ensure portability, serialization +v3 XDR is always used with headers skipped (as these contain R version +and encoding information).} + +\item{convert}{[default TRUE] if TRUE, the hash is converted to its hex +representation as a character string, if FALSE, output directly as a raw +vector, or if NA, a vector of (32-bit) integer values.} + +\item{file}{character file name / path. If specified, 'x' is ignored. The +file is hashed in a streaming fashion and may be larger than memory.} +} +\value{ +A character string, raw or integer vector depending on 'convert'. +} +\description{ +Returns a SHA-256 hash of the supplied R object or file. +} +\examples{ +# SHA-256 hash as character string: +sha256("secret base") + +# SHA-256 hash as raw vector: +sha256("secret base", convert = FALSE) + +# SHA-256 hash a file: +file <- tempfile(); cat("secret base", file = file) +sha256(file = file) +unlink(file) + +} diff --git a/man/sha3.Rd b/man/sha3.Rd index 8f48c4d..7d59b5a 100644 --- a/man/sha3.Rd +++ b/man/sha3.Rd @@ -2,12 +2,9 @@ % Please edit documentation in R/base.R \name{sha3} \alias{sha3} -\alias{sha3file} -\title{Cryptographic Hashing Using the SHA-3 Algorithm} +\title{Cryptographic Hashing Using the SHA-3 Algorithms} \usage{ -sha3(x, bits = 256L, convert = TRUE) - -sha3file(file, bits = 256L, convert = TRUE) +sha3(x, bits = 256L, convert = TRUE, file) } \arguments{ \item{x}{R object to hash. A character string or raw vector (without @@ -26,8 +23,8 @@ Must be between 8 and 2^24 and coercible to integer.} representation as a character string, if FALSE, output directly as a raw vector, or if NA, a vector of (32-bit) integer values.} -\item{file}{character file name / path. The file is hashed in a streaming -fashion and does not need to fit in memory.} +\item{file}{character file name / path. If specified, 'x' is ignored. The +file is hashed in a streaming fashion and may be larger than memory.} } \value{ A character string, raw or integer vector depending on 'convert'. @@ -61,7 +58,7 @@ sha3("secret base", bits = 32L, convert = NA) # SHA3-256 hash a file: file <- tempfile(); cat("secret base", file = file) -sha3file(file) +sha3(file = file) unlink(file) - + } diff --git a/src/init.c b/src/init.c index 9b958ea..913228c 100644 --- a/src/init.c +++ b/src/init.c @@ -21,6 +21,8 @@ static const R_CallMethodDef callMethods[] = { {"secretbase_sha3", (DL_FUNC) &secretbase_sha3, 3}, {"secretbase_sha3_file", (DL_FUNC) &secretbase_sha3_file, 3}, + {"secretbase_sha256", (DL_FUNC) &secretbase_sha256, 2}, + {"secretbase_sha256_file", (DL_FUNC) &secretbase_sha256_file, 2}, {NULL, NULL, 0} }; diff --git a/src/secret.c b/src/secret.c index 97c7639..4dbcfdd 100644 --- a/src/secret.c +++ b/src/secret.c @@ -193,11 +193,352 @@ static void mbedtls_sha3_finish(mbedtls_sha3_context *ctx, uint8_t *output, size } +// secretbase - sha256 implementation ------------------------------------------ + +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +static uint32_t mbedtls_get_unaligned_uint32(const void *p) { + uint32_t r; + memcpy(&r, p, sizeof(r)); + return r; +} + +static void mbedtls_put_unaligned_uint32(void *p, uint32_t x) { + memcpy(p, &x, sizeof(x)); +} + +#if defined(__GNUC__) && defined(__GNUC_PREREQ) +#if __GNUC_PREREQ(4, 8) +#define MBEDTLS_BSWAP16 __builtin_bswap16 +#endif /* __GNUC_PREREQ(4,8) */ +#if __GNUC_PREREQ(4, 3) +#define MBEDTLS_BSWAP32 __builtin_bswap32 +#define MBEDTLS_BSWAP64 __builtin_bswap64 +#endif /* __GNUC_PREREQ(4,3) */ +#endif /* defined(__GNUC__) && defined(__GNUC_PREREQ) */ + +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_bswap16) && !defined(MBEDTLS_BSWAP16) +#define MBEDTLS_BSWAP16 __builtin_bswap16 +#endif /* __has_builtin(__builtin_bswap16) */ +#if __has_builtin(__builtin_bswap32) && !defined(MBEDTLS_BSWAP32) +#define MBEDTLS_BSWAP32 __builtin_bswap32 +#endif /* __has_builtin(__builtin_bswap32) */ +#if __has_builtin(__builtin_bswap64) && !defined(MBEDTLS_BSWAP64) +#define MBEDTLS_BSWAP64 __builtin_bswap64 +#endif /* __has_builtin(__builtin_bswap64) */ +#endif /* defined(__clang__) && defined(__has_builtin) */ + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 410000) && !defined(MBEDTLS_BSWAP32) +#if defined(__ARM_ACLE) +#include +#endif +#define MBEDTLS_BSWAP32 __rev +#endif + +#if defined(__IAR_SYSTEMS_ICC__) +#if defined(__ARM_ACLE) +#include +#define MBEDTLS_BSWAP16(x) ((uint16_t) __rev16((uint32_t) (x))) +#define MBEDTLS_BSWAP32 __rev +#define MBEDTLS_BSWAP64 __revll +#endif +#endif + +#if !defined(MBEDTLS_BSWAP16) +static inline uint16_t mbedtls_bswap16(uint16_t x) { + return + (x & 0x00ff) << 8 | + (x & 0xff00) >> 8; +} +#define MBEDTLS_BSWAP16 mbedtls_bswap16 +#endif /* !defined(MBEDTLS_BSWAP16) */ + +#if !defined(MBEDTLS_BSWAP32) +static inline uint32_t mbedtls_bswap32(uint32_t x) { + return + (x & 0x000000ff) << 24 | + (x & 0x0000ff00) << 8 | + (x & 0x00ff0000) >> 8 | + (x & 0xff000000) >> 24; +} +#define MBEDTLS_BSWAP32 mbedtls_bswap32 +#endif /* !defined(MBEDTLS_BSWAP32) */ + +#if !defined(MBEDTLS_BSWAP64) +static inline uint64_t mbedtls_bswap64(uint64_t x) { + return + (x & 0x00000000000000ffULL) << 56 | + (x & 0x000000000000ff00ULL) << 40 | + (x & 0x0000000000ff0000ULL) << 24 | + (x & 0x00000000ff000000ULL) << 8 | + (x & 0x000000ff00000000ULL) >> 8 | + (x & 0x0000ff0000000000ULL) >> 24 | + (x & 0x00ff000000000000ULL) >> 40 | + (x & 0xff00000000000000ULL) >> 56; +} +#define MBEDTLS_BSWAP64 mbedtls_bswap64 +#endif /* !defined(MBEDTLS_BSWAP64) */ + +#define MBEDTLS_GET_UINT32_BE(data, offset) \ +((MBEDTLS_IS_BIG_ENDIAN) \ + ? mbedtls_get_unaligned_uint32((data) + (offset)) \ + : MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))) \ +) + +#define MBEDTLS_PUT_UINT32_BE(n, data, offset) \ +{ \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), (uint32_t) (n)); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t) (n))); \ + } \ +} \ + +static void mbedtls_sha256_init(mbedtls_sha256_context *ctx) { + + memset(ctx, 0, sizeof(mbedtls_sha256_context)); + +} + +static void mbedtls_sha256_starts(mbedtls_sha256_context *ctx) { + + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; + +} + +static const uint32_t K[] = + { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, + }; + +#define SHR(x, n) (((x) & 0xFFFFFFFF) >> (n)) +#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n)))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) + +#define F0(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) + +#define R(t) \ +( \ + local.W[t] = S1(local.W[(t) - 2]) + local.W[(t) - 7] + \ + S0(local.W[(t) - 15]) + local.W[(t) - 16] \ +) + +#define P(a, b, c, d, e, f, g, h, x, K) \ +do \ +{ \ + local.temp1 = (h) + S3(e) + F1((e), (f), (g)) + (K) + (x); \ + local.temp2 = S2(a) + F0((a), (b), (c)); \ + (d) += local.temp1; (h) = local.temp1 + local.temp2; \ +} while (0) + +static void mbedtls_internal_sha256_process(mbedtls_sha256_context *ctx, + const unsigned char data[64]) { + + struct { + uint32_t temp1, temp2, W[64]; + uint32_t A[8]; + } local; + + unsigned int i; + + for (i = 0; i < 8; i++) { + local.A[i] = ctx->state[i]; + } + + for (i = 0; i < 16; i++) { + local.W[i] = MBEDTLS_GET_UINT32_BE(data, 4 * i); + } + + for (i = 0; i < 16; i += 8) { + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], local.W[i+0], K[i+0]); + P(local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], + local.A[4], local.A[5], local.A[6], local.W[i+1], K[i+1]); + P(local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], + local.A[3], local.A[4], local.A[5], local.W[i+2], K[i+2]); + P(local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], + local.A[2], local.A[3], local.A[4], local.W[i+3], K[i+3]); + P(local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], + local.A[1], local.A[2], local.A[3], local.W[i+4], K[i+4]); + P(local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], + local.A[0], local.A[1], local.A[2], local.W[i+5], K[i+5]); + P(local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], + local.A[7], local.A[0], local.A[1], local.W[i+6], K[i+6]); + P(local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], + local.A[6], local.A[7], local.A[0], local.W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], R(i+0), K[i+0]); + P(local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], + local.A[4], local.A[5], local.A[6], R(i+1), K[i+1]); + P(local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], + local.A[3], local.A[4], local.A[5], R(i+2), K[i+2]); + P(local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], + local.A[2], local.A[3], local.A[4], R(i+3), K[i+3]); + P(local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], + local.A[1], local.A[2], local.A[3], R(i+4), K[i+4]); + P(local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], + local.A[0], local.A[1], local.A[2], R(i+5), K[i+5]); + P(local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], + local.A[7], local.A[0], local.A[1], R(i+6), K[i+6]); + P(local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], + local.A[6], local.A[7], local.A[0], R(i+7), K[i+7]); + } + + for (i = 0; i < 8; i++) { + ctx->state[i] += local.A[i]; + } + +} + +static size_t mbedtls_internal_sha256_process_many(mbedtls_sha256_context *ctx, + const uint8_t *data, + size_t len) { + + size_t processed = 0; + while (len >= 64) { + mbedtls_internal_sha256_process(ctx, data); + data += 64; + len -= 64; + processed += 64; + } + + return processed; + +} + +static void mbedtls_sha256_update(mbedtls_sha256_context *ctx, + const unsigned char *input, + size_t ilen) { + + size_t fill; + uint32_t left; + + if (ilen == 0) { + return; + } + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + mbedtls_internal_sha256_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + size_t processed = mbedtls_internal_sha256_process_many(ctx, input, ilen); + input += processed; + ilen -= processed; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + +} + +static void mbedtls_sha256_finish(mbedtls_sha256_context *ctx, + unsigned char *output) { + + uint32_t used; + uint32_t high, low; + + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if (used <= 56) { + memset(ctx->buffer + used, 0, 56 - used); + } else { + memset(ctx->buffer + used, 0, 64 - used); + mbedtls_internal_sha256_process(ctx, ctx->buffer); + memset(ctx->buffer, 0, 56); + } + + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + MBEDTLS_PUT_UINT32_BE(high, ctx->buffer, 56); + MBEDTLS_PUT_UINT32_BE(low, ctx->buffer, 60); + + mbedtls_internal_sha256_process(ctx, ctx->buffer); + + MBEDTLS_PUT_UINT32_BE(ctx->state[0], output, 0); + MBEDTLS_PUT_UINT32_BE(ctx->state[1], output, 4); + MBEDTLS_PUT_UINT32_BE(ctx->state[2], output, 8); + MBEDTLS_PUT_UINT32_BE(ctx->state[3], output, 12); + MBEDTLS_PUT_UINT32_BE(ctx->state[4], output, 16); + MBEDTLS_PUT_UINT32_BE(ctx->state[5], output, 20); + MBEDTLS_PUT_UINT32_BE(ctx->state[6], output, 24); + MBEDTLS_PUT_UINT32_BE(ctx->state[7], output, 28); + +} + // secretbase - internals ------------------------------------------------------ +static void * (*const volatile secure_memset)(void *, int, size_t) = memset; + static void clear_buffer(void *buf, size_t sz) { - void * (*const volatile secure_memset)(void *, int, size_t) = memset; secure_memset(buf, 0, sz); } @@ -205,53 +546,58 @@ static void clear_buffer(void *buf, size_t sz) { static void hash_bytes(R_outpstream_t stream, void *src, int len) { secretbase_context *sctx = (secretbase_context *) stream->data; - sctx->skip ? (void) sctx->skip-- : mbedtls_sha3_update(sctx->ctx, (uint8_t *) src, (size_t) len); - + sctx->skip ? (void) sctx->skip-- : sctx->update(sctx->ctx, (uint8_t *) src, (size_t) len); + } -static void hash_file(mbedtls_sha3_context *ctx, const SEXP x) { +void hash_file(const update_func update, void *ctx, const SEXP x) { + if (TYPEOF(x) != STRSXP) + Rf_error("'file' must be specified as a character string"); const char *file = R_ExpandFileName(CHAR(STRING_ELT(x, 0))); unsigned char buf[SB_BUF_SIZE]; - FILE *fp; + FILE *f; size_t cur; - if ((fp = fopen(file, "rb")) == NULL) + if ((f = fopen(file, "rb")) == NULL) Rf_error("file not found or no read permission at '%s'", file); - while ((cur = fread(buf, sizeof(char), SB_BUF_SIZE, fp))) { - mbedtls_sha3_update(ctx, buf, cur); + + setbuf(f, NULL); + + while ((cur = fread(buf, sizeof(char), SB_BUF_SIZE, f))) { + update(ctx, buf, cur); } - clear_buffer(&buf, SB_BUF_SIZE); - if (ferror(fp)) { - fclose(fp); + if (ferror(f)) { + fclose(f); Rf_error("file read error at '%s'", file); } - fclose(fp); + fclose(f); } -static void hash_object(mbedtls_sha3_context *ctx, const SEXP x) { +void hash_object(const update_func update, void *ctx, const SEXP x) { switch (TYPEOF(x)) { case STRSXP: if (XLENGTH(x) == 1 && ATTRIB(x) == R_NilValue) { const char *s = CHAR(STRING_ELT(x, 0)); - mbedtls_sha3_update(ctx, (uint8_t *) s, strlen(s)); + update(ctx, (uint8_t *) s, strlen(s)); return; } break; case RAWSXP: if (ATTRIB(x) == R_NilValue) { - mbedtls_sha3_update(ctx, (uint8_t *) STDVEC_DATAPTR(x), (size_t) XLENGTH(x)); + update(ctx, (uint8_t *) STDVEC_DATAPTR(x), (size_t) XLENGTH(x)); return; } break; } secretbase_context sctx; - sctx.ctx = ctx; sctx.skip = SB_SERIAL_HEADERS; + sctx.ctx = ctx; + sctx.update = update; struct R_outpstream_st output_stream; R_InitOutPStream( @@ -268,30 +614,9 @@ static void hash_object(mbedtls_sha3_context *ctx, const SEXP x) { } -static SEXP secretbase_sha3_impl(const SEXP x, const SEXP bits, const SEXP convert, - void (*const hash_func)(mbedtls_sha3_context *, SEXP)) { +SEXP hash_to_sexp(unsigned char *buf, size_t sz, int conv) { - const int conv = LOGICAL(convert)[0]; - const int bt = Rf_asInteger(bits); - if (bt < 8 || bt > (1 << 24)) - Rf_error("'bits' outside valid range of 8 to 2^24"); - const size_t sz = (size_t) (bt / 8); - unsigned char buf[sz]; SEXP out; - - mbedtls_sha3_id id = bt == 256 ? MBEDTLS_SHA3_256 : - bt == 512 ? MBEDTLS_SHA3_512 : - bt == 224 ? MBEDTLS_SHA3_224 : - bt == 384 ? MBEDTLS_SHA3_384 : - MBEDTLS_SHA3_SHAKE256; - - mbedtls_sha3_context ctx; - mbedtls_sha3_init(&ctx); - mbedtls_sha3_starts(&ctx, id); - hash_func(&ctx, x); - mbedtls_sha3_finish(&ctx, buf, sz); - clear_buffer(&ctx, sizeof(mbedtls_sha3_context)); - if (conv == 0) { out = Rf_allocVector(RAWSXP, sz); memcpy(STDVEC_DATAPTR(out), buf, sz); @@ -312,6 +637,51 @@ static SEXP secretbase_sha3_impl(const SEXP x, const SEXP bits, const SEXP conve } +static SEXP secretbase_sha3_impl(const SEXP x, const SEXP bits, const SEXP convert, + const hash_func hfunc) { + + const int conv = LOGICAL(convert)[0]; + const int bt = Rf_asInteger(bits); + if (bt < 8 || bt > (1 << 24)) + Rf_error("'bits' outside valid range of 8 to 2^24"); + const size_t sz = (size_t) (bt / 8); + unsigned char buf[sz]; + + mbedtls_sha3_id id = bt == 256 ? MBEDTLS_SHA3_256 : + bt == 512 ? MBEDTLS_SHA3_512 : + bt == 224 ? MBEDTLS_SHA3_224 : + bt == 384 ? MBEDTLS_SHA3_384 : + MBEDTLS_SHA3_SHAKE256; + + mbedtls_sha3_context ctx; + mbedtls_sha3_init(&ctx); + mbedtls_sha3_starts(&ctx, id); + hfunc((update_func) mbedtls_sha3_update, &ctx, x); + mbedtls_sha3_finish(&ctx, buf, sz); + clear_buffer(&ctx, sizeof(mbedtls_sha3_context)); + + return hash_to_sexp(buf, sz, conv); + +} + +static SEXP secretbase_sha256_impl(const SEXP x, const SEXP convert, + const hash_func hfunc) { + + const int conv = LOGICAL(convert)[0]; + const size_t sz = 32; + unsigned char buf[sz]; + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + mbedtls_sha256_starts(&ctx); + hfunc((update_func) mbedtls_sha256_update, &ctx, x); + mbedtls_sha256_finish(&ctx, buf); + clear_buffer(&ctx, sizeof(mbedtls_sha256_context)); + + return hash_to_sexp(buf, sz, conv); + +} + // secretbase - exported functions --------------------------------------------- SEXP secretbase_sha3(SEXP x, SEXP bits, SEXP convert) { @@ -325,3 +695,15 @@ SEXP secretbase_sha3_file(SEXP x, SEXP bits, SEXP convert) { return secretbase_sha3_impl(x, bits, convert, hash_file); } + +SEXP secretbase_sha256(SEXP x, SEXP convert) { + + return secretbase_sha256_impl(x, convert, hash_object); + +} + +SEXP secretbase_sha256_file(SEXP x, SEXP convert) { + + return secretbase_sha256_impl(x, convert, hash_file); + +} diff --git a/src/secret.h b/src/secret.h index e6dd6f6..cf0fb19 100644 --- a/src/secret.h +++ b/src/secret.h @@ -30,6 +30,12 @@ #define SB_SERIAL_HEADERS 6 #define SB_BUF_SIZE 4096 +#ifdef WORDS_BIGENDIAN +# define MBEDTLS_IS_BIG_ENDIAN 1 +#else +# define MBEDTLS_IS_BIG_ENDIAN 0 +#endif + typedef enum { MBEDTLS_SHA3_SHAKE256 = 0, MBEDTLS_SHA3_224, @@ -55,12 +61,28 @@ typedef struct mbedtls_sha3_context { uint16_t max_block_size; } mbedtls_sha3_context; +typedef struct mbedtls_sha256_context { + unsigned char buffer[64]; + uint32_t total[2]; + uint32_t state[8]; +} mbedtls_sha256_context; + +typedef void (*update_func)(void *, const uint8_t *, size_t); +typedef void (*hash_func)(const update_func, void *, SEXP); + typedef struct secretbase_context { int skip; - mbedtls_sha3_context *ctx; + void *ctx; + update_func update; } secretbase_context; +void hash_object(const update_func, void *, const SEXP); +void hash_file(const update_func, void *, const SEXP); +SEXP hash_to_sexp(unsigned char *, size_t, int); + SEXP secretbase_sha3(SEXP, SEXP, SEXP); SEXP secretbase_sha3_file(SEXP, SEXP, SEXP); +SEXP secretbase_sha256(SEXP, SEXP); +SEXP secretbase_sha256_file(SEXP, SEXP); #endif diff --git a/tests/tests.R b/tests/tests.R index d3f1c48..f795c7d 100644 --- a/tests/tests.R +++ b/tests/tests.R @@ -4,11 +4,12 @@ test_equal <- function(x, y) invisible(x == y || stop("generated hash differs fr test_error <- function(x, e = "") invisible(grepl(e, tryCatch(x, error = identity)[["message"]], fixed = TRUE) || stop("expected error message '", e, "' not generated")) -# Known SHA-3 hashes from NIST: +# Known SHA hashes from NIST: test_equal(sha3("", 224), "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7") test_equal(sha3("", 256), "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") test_equal(sha3("", 384), "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004") test_equal(sha3("", 512), "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26") +test_equal(sha256(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") # SHA-3 tests: test_equal(sha3("secret base"), "a721d57570e7ce366adee2fccbe9770723c6e3622549c31c7cab9dbb4a795520") test_equal(sha3("secret base", bits = 224), "5511b3469d3f1a87b62ce8f0d2dc9510ec5e4547579b8afb32052f99") @@ -23,6 +24,7 @@ test_equal(sha3("secret base", bits = 32, convert = NA), -1044750695L) # Streaming serialization tests: test_equal(sha3(data.frame(a = 1, b = 2)), "05d4308e79d029b4af5604739ecc6c4efa1f602a23add0ed2d247b7407d4832f") test_equal(sha3(c("secret", "base")), "d906024c71828a10e28865a80f5e81d2cb5cd74067d44852d7039813ba62b0b6") +test_equal(sha3(`attr<-`("base", "secret", "base")), "eac181cb1c64e7196c458d40cebfb8bbd6d34a1d728936a2e689465879240e2a") test_equal(sha3(NULL), "b3e37e4c5def1bfb2841b79ef8503b83d1fed46836b5b913d7c16de92966dcee") test_equal(sha3(substitute()), "9d31eb41cfb721b8040c52d574df1aacfc381d371c2b933f90792beba5160a57") test_equal(sha3(`class<-`(sha3(character(), bits = 192, convert = FALSE), "hash"), bits = "32", convert = NA), -111175135L) @@ -30,12 +32,28 @@ test_equal(sha3(`class<-`(sha3(character(), bits = 192, convert = FALSE), "hash" test_error(sha3("secret base", bits = 0), "'bits' outside valid range of 8 to 2^24") test_error(sha3("secret base", bits = -1), "'bits' outside valid range of 8 to 2^24") test_error(sha3("secret base", bits = 2^24 + 1), "'bits' outside valid range of 8 to 2^24") +test_error(sha3(file = NULL), "'file' must be specified as a character string") # File interface tests: hash_func <- function(file, string) { on.exit(unlink(file)) cat(string, file = file) - sha3file(file) + sha3(file = file) } test_equal(hash_func(tempfile(), "secret base"), "a721d57570e7ce366adee2fccbe9770723c6e3622549c31c7cab9dbb4a795520") test_error(hash_func("", ""), "file not found or no read permission") -if (.Platform[["OS.type"]] == "unix") test_error(sha3file("~/"), "file read error") +if (.Platform[["OS.type"]] == "unix") test_error(sha3(file = "~/"), "file read error") +# SHA-256 tests: +test_equal(sha256("secret base"), "1951c1ca3d50e95e6ede2b1c26fefd0f0e8eba1e51a837f8ccefb583a2b686fe") +test_equal(sha256("secret base", convert = NA)[2L], 1592348733L) +test_that(sha256("secret base", convert = FALSE), is.raw) +test_equal(sha256(data.frame(a = 1, b = 2)), "189874c3ac59edecb4eab95a2d7c1bbb293a6ccd04e3da5b28daca91ebc7f15b") +hash_func <- function(file, string) { + on.exit(unlink(file)) + cat(string, file = file) + sha256(file = file) +} +test_equal(hash_func(tempfile(), "secret base"), "1951c1ca3d50e95e6ede2b1c26fefd0f0e8eba1e51a837f8ccefb583a2b686fe") +test_error(hash_func("", ""), "file not found or no read permission") +if (.Platform[["OS.type"]] == "unix") test_error(sha256(file = "~/"), "file read error") +test_equal(sha256(paste(1:888, collapse = "")), "ec5df945d0ff0c927812ec503fe9ffd5cbdf7cf79b5391ad5002b3a80760183b") +test_equal(sha256(NULL), "71557d1c8bac9bbe3cbec8d00bb223a2f372279827064095447e569fbf5a760a")