├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── blake2-impl.h ├── blake2.h ├── blake2b-ref.c ├── blake2s-ref.c ├── config.m4 ├── config.w32 ├── php_blake2.c ├── php_blake2.h └── tests ├── b2sum.phpt ├── blake2_error.phpt ├── blake2_file.phpt ├── blake2_hash.phpt ├── blake2b_hash.phpt ├── blake2s_hash.phpt └── sample.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .libs/ 3 | Makefile 4 | Makefile.fragments 5 | Makefile.global 6 | Makefile.objects 7 | acinclude.m4 8 | aclocal.m4 9 | autom4te.cache/ 10 | blake2.la 11 | blake2b-ref.lo 12 | blake2s-ref.lo 13 | build/ 14 | config.guess 15 | config.h 16 | config.h.in 17 | config.log 18 | config.nice 19 | config.status 20 | config.sub 21 | configure 22 | configure.in 23 | install-sh 24 | libtool 25 | ltmain.sh 26 | missing 27 | mkinstalldirs 28 | modules/ 29 | include/ 30 | php_blake2.lo 31 | run-tests.php 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '5.4' 4 | - '5.5' 5 | - '5.6' 6 | - '7.0' 7 | - '7.1' 8 | - '7.2' 9 | env: 10 | - NO_INTERACTION=1 11 | before_script: 12 | - phpize 13 | - ./configure --enable-blake2 14 | - make 15 | script: make test 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-present strawbrary 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP BLAKE2 Extension 2 | ============================ 3 | 4 | [![Build Status](https://travis-ci.org/strawbrary/php-blake2.svg?branch=master)](https://travis-ci.org/strawbrary/php-blake2) 5 | 6 | BLAKE2 is an improved version of BLAKE, one the finalists in the NIST SHA-3 competition. Like BLAKE or SHA-3, BLAKE2 offers the highest security, yet is fast as MD5 on 64-bit platforms and requires at least 33% less RAM than SHA-2 or SHA-3 on low-end systems. This implementation uses the BLAKE2b variant of the algorithm which is optimized for 64-bit systems. The algorithm was designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian Winnerlein. 7 | 8 | Installation 9 | ------------ 10 | Clone the repository and compile it: 11 | ```sh 12 | $ git clone git@github.com:strawbrary/php-blake2.git 13 | $ cd php-blake2 14 | $ phpize 15 | $ ./configure --enable-blake2 16 | $ make && sudo make install 17 | ``` 18 | 19 | Enable the extension by adding the following line to your php.ini file: 20 | 21 | ```sh 22 | extension=blake2.so 23 | ``` 24 | 25 | You may need to restart your web server to load the extension. 26 | 27 | 28 | Usage 29 | ---- 30 | ```php 31 | string blake2 ( string $str [, int $outputSize = 64, string $key, bool $rawOutput = false ] ) 32 | ``` 33 | 34 | * $str: The string to hash 35 | * $outputSize: The length of the output hash (can be between 1 and 64) 36 | * $key: Turns the output into a keyed hash using the specified key 37 | * $rawOutput: If set to true, then the hash is returned in raw binary format 38 | 39 | * Return value: A hex string containing the BLAKE2 hash of the input string 40 | 41 | ```php 42 | string blake2b ( string $str [, int $outputSize = 64, string $key, bool $rawOutput = false ] ) 43 | ``` 44 | 45 | is an alias to `blake2` 46 | 47 | 48 | ```php 49 | string blake2s ( string $str [, int $outputSize = 32, string $key, bool $rawOutput = false ] ) 50 | ``` 51 | 52 | * $str: The string to hash 53 | * $outputSize: The length of the output hash (can be between 1 and 32) 54 | * $key: Turns the output into a keyed hash using the specified key 55 | * $rawOutput: If set to true, then the hash is returned in raw binary format 56 | 57 | * Return value: A hex string containing the BLAKE2s hash of the input string 58 | 59 | ```php 60 | string blake2_file ( string $filename [, bool $rawOutput = false ] ) 61 | ``` 62 | 63 | * $filename: The filename of the file to hash 64 | * $rawOutput: If set to true, then the hash is returned in raw binary format 65 | 66 | * Return value: A hex string containing the BLAKE2 hash of the input file 67 | 68 | ```php 69 | string b2sum ( string $filename [, bool $rawOutput = false ] ) 70 | ``` 71 | 72 | is an alias to `blake2_file` 73 | 74 | 75 | Examples 76 | -------- 77 | ```php 78 | echo blake2(''); 79 | ``` 80 | 81 | 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce 82 | 83 | ```php 84 | echo blake2('Hello world', 20); 85 | ``` 86 | 87 | 5ad31b81fc4dde5554e36af1e884d83ff5b24eb0 88 | 89 | ```php 90 | echo blake2('Hello world', 20, 'foobar'); 91 | ``` 92 | 93 | 5b4bbc84b59ab5d9146089b143fd52f38638dcac 94 | 95 | 96 | ```php 97 | echo blake2s(''); 98 | ``` 99 | 100 | Outputs : 69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9 101 | 102 | ```php 103 | echo b2sum('tests/sample.txt'); 104 | ``` 105 | 106 | Outputs : a61b779ff667fbcc4775cbb02cd0763b9b5312fe6359a44a003f582ce6897c81a38a876122ce91dfec547d582fe269f6ea9bd291b60bccf95006dac10a4316f2 107 | 108 | More Info 109 | --------- 110 | https://blake2.net/ 111 | -------------------------------------------------------------------------------- /blake2-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | #ifndef BLAKE2_IMPL_H 16 | #define BLAKE2_IMPL_H 17 | 18 | #include 19 | #include 20 | 21 | #if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) 22 | #if defined(_MSC_VER) 23 | #define BLAKE2_INLINE __inline 24 | #elif defined(__GNUC__) 25 | #define BLAKE2_INLINE __inline__ 26 | #else 27 | #define BLAKE2_INLINE 28 | #endif 29 | #else 30 | #define BLAKE2_INLINE inline 31 | #endif 32 | 33 | static BLAKE2_INLINE uint32_t load32( const void *src ) 34 | { 35 | #if defined(NATIVE_LITTLE_ENDIAN) 36 | uint32_t w; 37 | memcpy(&w, src, sizeof w); 38 | return w; 39 | #else 40 | const uint8_t *p = ( const uint8_t * )src; 41 | return (( uint32_t )( p[0] ) << 0) | 42 | (( uint32_t )( p[1] ) << 8) | 43 | (( uint32_t )( p[2] ) << 16) | 44 | (( uint32_t )( p[3] ) << 24) ; 45 | #endif 46 | } 47 | 48 | static BLAKE2_INLINE uint64_t load64( const void *src ) 49 | { 50 | #if defined(NATIVE_LITTLE_ENDIAN) 51 | uint64_t w; 52 | memcpy(&w, src, sizeof w); 53 | return w; 54 | #else 55 | const uint8_t *p = ( const uint8_t * )src; 56 | return (( uint64_t )( p[0] ) << 0) | 57 | (( uint64_t )( p[1] ) << 8) | 58 | (( uint64_t )( p[2] ) << 16) | 59 | (( uint64_t )( p[3] ) << 24) | 60 | (( uint64_t )( p[4] ) << 32) | 61 | (( uint64_t )( p[5] ) << 40) | 62 | (( uint64_t )( p[6] ) << 48) | 63 | (( uint64_t )( p[7] ) << 56) ; 64 | #endif 65 | } 66 | 67 | static BLAKE2_INLINE uint16_t load16( const void *src ) 68 | { 69 | #if defined(NATIVE_LITTLE_ENDIAN) 70 | uint16_t w; 71 | memcpy(&w, src, sizeof w); 72 | return w; 73 | #else 74 | const uint8_t *p = ( const uint8_t * )src; 75 | return (( uint16_t )( p[0] ) << 0) | 76 | (( uint16_t )( p[1] ) << 8) ; 77 | #endif 78 | } 79 | 80 | static BLAKE2_INLINE void store16( void *dst, uint16_t w ) 81 | { 82 | #if defined(NATIVE_LITTLE_ENDIAN) 83 | memcpy(dst, &w, sizeof w); 84 | #else 85 | uint8_t *p = ( uint8_t * )dst; 86 | *p++ = ( uint8_t )w; w >>= 8; 87 | *p++ = ( uint8_t )w; 88 | #endif 89 | } 90 | 91 | static BLAKE2_INLINE void store32( void *dst, uint32_t w ) 92 | { 93 | #if defined(NATIVE_LITTLE_ENDIAN) 94 | memcpy(dst, &w, sizeof w); 95 | #else 96 | uint8_t *p = ( uint8_t * )dst; 97 | p[0] = (uint8_t)(w >> 0); 98 | p[1] = (uint8_t)(w >> 8); 99 | p[2] = (uint8_t)(w >> 16); 100 | p[3] = (uint8_t)(w >> 24); 101 | #endif 102 | } 103 | 104 | static BLAKE2_INLINE void store64( void *dst, uint64_t w ) 105 | { 106 | #if defined(NATIVE_LITTLE_ENDIAN) 107 | memcpy(dst, &w, sizeof w); 108 | #else 109 | uint8_t *p = ( uint8_t * )dst; 110 | p[0] = (uint8_t)(w >> 0); 111 | p[1] = (uint8_t)(w >> 8); 112 | p[2] = (uint8_t)(w >> 16); 113 | p[3] = (uint8_t)(w >> 24); 114 | p[4] = (uint8_t)(w >> 32); 115 | p[5] = (uint8_t)(w >> 40); 116 | p[6] = (uint8_t)(w >> 48); 117 | p[7] = (uint8_t)(w >> 56); 118 | #endif 119 | } 120 | 121 | static BLAKE2_INLINE uint64_t load48( const void *src ) 122 | { 123 | const uint8_t *p = ( const uint8_t * )src; 124 | return (( uint64_t )( p[0] ) << 0) | 125 | (( uint64_t )( p[1] ) << 8) | 126 | (( uint64_t )( p[2] ) << 16) | 127 | (( uint64_t )( p[3] ) << 24) | 128 | (( uint64_t )( p[4] ) << 32) | 129 | (( uint64_t )( p[5] ) << 40) ; 130 | } 131 | 132 | static BLAKE2_INLINE void store48( void *dst, uint64_t w ) 133 | { 134 | uint8_t *p = ( uint8_t * )dst; 135 | p[0] = (uint8_t)(w >> 0); 136 | p[1] = (uint8_t)(w >> 8); 137 | p[2] = (uint8_t)(w >> 16); 138 | p[3] = (uint8_t)(w >> 24); 139 | p[4] = (uint8_t)(w >> 32); 140 | p[5] = (uint8_t)(w >> 40); 141 | } 142 | 143 | static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) 144 | { 145 | return ( w >> c ) | ( w << ( 32 - c ) ); 146 | } 147 | 148 | static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) 149 | { 150 | return ( w >> c ) | ( w << ( 64 - c ) ); 151 | } 152 | 153 | /* prevents compiler optimizing out memset() */ 154 | static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) 155 | { 156 | static void *(*const volatile memset_v)(void *, int, size_t) = &memset; 157 | memset_v(v, 0, n); 158 | } 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /blake2.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | #ifndef BLAKE2_H 16 | #define BLAKE2_H 17 | 18 | #include 19 | #include 20 | 21 | #if defined(_MSC_VER) 22 | #define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) 23 | #else 24 | #define BLAKE2_PACKED(x) x __attribute__((packed)) 25 | #endif 26 | 27 | #if defined(__cplusplus) 28 | extern "C" { 29 | #endif 30 | 31 | enum blake2s_constant 32 | { 33 | BLAKE2S_BLOCKBYTES = 64, 34 | BLAKE2S_OUTBYTES = 32, 35 | BLAKE2S_KEYBYTES = 32, 36 | BLAKE2S_SALTBYTES = 8, 37 | BLAKE2S_PERSONALBYTES = 8 38 | }; 39 | 40 | enum blake2b_constant 41 | { 42 | BLAKE2B_BLOCKBYTES = 128, 43 | BLAKE2B_OUTBYTES = 64, 44 | BLAKE2B_KEYBYTES = 64, 45 | BLAKE2B_SALTBYTES = 16, 46 | BLAKE2B_PERSONALBYTES = 16 47 | }; 48 | 49 | typedef struct blake2s_state__ 50 | { 51 | uint32_t h[8]; 52 | uint32_t t[2]; 53 | uint32_t f[2]; 54 | uint8_t buf[BLAKE2S_BLOCKBYTES]; 55 | size_t buflen; 56 | size_t outlen; 57 | uint8_t last_node; 58 | } blake2s_state; 59 | 60 | typedef struct blake2b_state__ 61 | { 62 | uint64_t h[8]; 63 | uint64_t t[2]; 64 | uint64_t f[2]; 65 | uint8_t buf[BLAKE2B_BLOCKBYTES]; 66 | size_t buflen; 67 | size_t outlen; 68 | uint8_t last_node; 69 | } blake2b_state; 70 | 71 | typedef struct blake2sp_state__ 72 | { 73 | blake2s_state S[8][1]; 74 | blake2s_state R[1]; 75 | uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; 76 | size_t buflen; 77 | size_t outlen; 78 | } blake2sp_state; 79 | 80 | typedef struct blake2bp_state__ 81 | { 82 | blake2b_state S[4][1]; 83 | blake2b_state R[1]; 84 | uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; 85 | size_t buflen; 86 | size_t outlen; 87 | } blake2bp_state; 88 | 89 | 90 | BLAKE2_PACKED(struct blake2s_param__ 91 | { 92 | uint8_t digest_length; /* 1 */ 93 | uint8_t key_length; /* 2 */ 94 | uint8_t fanout; /* 3 */ 95 | uint8_t depth; /* 4 */ 96 | uint32_t leaf_length; /* 8 */ 97 | uint32_t node_offset; /* 12 */ 98 | uint16_t xof_length; /* 14 */ 99 | uint8_t node_depth; /* 15 */ 100 | uint8_t inner_length; /* 16 */ 101 | /* uint8_t reserved[0]; */ 102 | uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ 103 | uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ 104 | }); 105 | 106 | typedef struct blake2s_param__ blake2s_param; 107 | 108 | BLAKE2_PACKED(struct blake2b_param__ 109 | { 110 | uint8_t digest_length; /* 1 */ 111 | uint8_t key_length; /* 2 */ 112 | uint8_t fanout; /* 3 */ 113 | uint8_t depth; /* 4 */ 114 | uint32_t leaf_length; /* 8 */ 115 | uint32_t node_offset; /* 12 */ 116 | uint32_t xof_length; /* 16 */ 117 | uint8_t node_depth; /* 17 */ 118 | uint8_t inner_length; /* 18 */ 119 | uint8_t reserved[14]; /* 32 */ 120 | uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ 121 | uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ 122 | }); 123 | 124 | typedef struct blake2b_param__ blake2b_param; 125 | 126 | typedef struct blake2xs_state__ 127 | { 128 | blake2s_state S[1]; 129 | blake2s_param P[1]; 130 | } blake2xs_state; 131 | 132 | typedef struct blake2xb_state__ 133 | { 134 | blake2b_state S[1]; 135 | blake2b_param P[1]; 136 | } blake2xb_state; 137 | 138 | /* Padded structs result in a compile-time error */ 139 | enum { 140 | BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), 141 | BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) 142 | }; 143 | 144 | /* Streaming API */ 145 | int blake2s_init( blake2s_state *S, size_t outlen ); 146 | int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); 147 | int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); 148 | int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); 149 | int blake2s_final( blake2s_state *S, void *out, size_t outlen ); 150 | 151 | int blake2b_init( blake2b_state *S, size_t outlen ); 152 | int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); 153 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); 154 | int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); 155 | int blake2b_final( blake2b_state *S, void *out, size_t outlen ); 156 | 157 | int blake2sp_init( blake2sp_state *S, size_t outlen ); 158 | int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); 159 | int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); 160 | int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); 161 | 162 | int blake2bp_init( blake2bp_state *S, size_t outlen ); 163 | int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); 164 | int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); 165 | int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); 166 | 167 | /* Variable output length API */ 168 | int blake2xs_init( blake2xs_state *S, const size_t outlen ); 169 | int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); 170 | int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); 171 | int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); 172 | 173 | int blake2xb_init( blake2xb_state *S, const size_t outlen ); 174 | int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); 175 | int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); 176 | int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); 177 | 178 | /* Simple API */ 179 | int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 180 | int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 181 | 182 | int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 183 | int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 184 | 185 | int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 186 | int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 187 | 188 | /* This is simply an alias for blake2b */ 189 | int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 190 | 191 | #if defined(__cplusplus) 192 | } 193 | #endif 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /blake2b-ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "blake2.h" 21 | #include "blake2-impl.h" 22 | 23 | static const uint64_t blake2b_IV[8] = 24 | { 25 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 26 | 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 27 | 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 28 | 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL 29 | }; 30 | 31 | static const uint8_t blake2b_sigma[12][16] = 32 | { 33 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 34 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , 35 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , 36 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , 37 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , 38 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , 39 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , 40 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , 41 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , 42 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , 43 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 44 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } 45 | }; 46 | 47 | 48 | static void blake2b_set_lastnode( blake2b_state *S ) 49 | { 50 | S->f[1] = (uint64_t)-1; 51 | } 52 | 53 | /* Some helper functions, not necessarily useful */ 54 | static int blake2b_is_lastblock( const blake2b_state *S ) 55 | { 56 | return S->f[0] != 0; 57 | } 58 | 59 | static void blake2b_set_lastblock( blake2b_state *S ) 60 | { 61 | if( S->last_node ) blake2b_set_lastnode( S ); 62 | 63 | S->f[0] = (uint64_t)-1; 64 | } 65 | 66 | static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) 67 | { 68 | S->t[0] += inc; 69 | S->t[1] += ( S->t[0] < inc ); 70 | } 71 | 72 | static void blake2b_init0( blake2b_state *S ) 73 | { 74 | size_t i; 75 | memset( S, 0, sizeof( blake2b_state ) ); 76 | 77 | for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; 78 | } 79 | 80 | /* init xors IV with input parameter block */ 81 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) 82 | { 83 | const uint8_t *p = ( const uint8_t * )( P ); 84 | size_t i; 85 | 86 | blake2b_init0( S ); 87 | 88 | /* IV XOR ParamBlock */ 89 | for( i = 0; i < 8; ++i ) 90 | S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); 91 | 92 | S->outlen = P->digest_length; 93 | return 0; 94 | } 95 | 96 | 97 | 98 | int blake2b_init( blake2b_state *S, size_t outlen ) 99 | { 100 | blake2b_param P[1]; 101 | 102 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 103 | 104 | P->digest_length = (uint8_t)outlen; 105 | P->key_length = 0; 106 | P->fanout = 1; 107 | P->depth = 1; 108 | store32( &P->leaf_length, 0 ); 109 | store32( &P->node_offset, 0 ); 110 | store32( &P->xof_length, 0 ); 111 | P->node_depth = 0; 112 | P->inner_length = 0; 113 | memset( P->reserved, 0, sizeof( P->reserved ) ); 114 | memset( P->salt, 0, sizeof( P->salt ) ); 115 | memset( P->personal, 0, sizeof( P->personal ) ); 116 | return blake2b_init_param( S, P ); 117 | } 118 | 119 | 120 | int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) 121 | { 122 | blake2b_param P[1]; 123 | 124 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 125 | 126 | if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; 127 | 128 | P->digest_length = (uint8_t)outlen; 129 | P->key_length = (uint8_t)keylen; 130 | P->fanout = 1; 131 | P->depth = 1; 132 | store32( &P->leaf_length, 0 ); 133 | store32( &P->node_offset, 0 ); 134 | store32( &P->xof_length, 0 ); 135 | P->node_depth = 0; 136 | P->inner_length = 0; 137 | memset( P->reserved, 0, sizeof( P->reserved ) ); 138 | memset( P->salt, 0, sizeof( P->salt ) ); 139 | memset( P->personal, 0, sizeof( P->personal ) ); 140 | 141 | if( blake2b_init_param( S, P ) < 0 ) return -1; 142 | 143 | { 144 | uint8_t block[BLAKE2B_BLOCKBYTES]; 145 | memset( block, 0, BLAKE2B_BLOCKBYTES ); 146 | memcpy( block, key, keylen ); 147 | blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); 148 | secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ 149 | } 150 | return 0; 151 | } 152 | 153 | #define G(r,i,a,b,c,d) \ 154 | do { \ 155 | a = a + b + m[blake2b_sigma[r][2*i+0]]; \ 156 | d = rotr64(d ^ a, 32); \ 157 | c = c + d; \ 158 | b = rotr64(b ^ c, 24); \ 159 | a = a + b + m[blake2b_sigma[r][2*i+1]]; \ 160 | d = rotr64(d ^ a, 16); \ 161 | c = c + d; \ 162 | b = rotr64(b ^ c, 63); \ 163 | } while(0) 164 | 165 | #define ROUND(r) \ 166 | do { \ 167 | G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ 168 | G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ 169 | G(r,2,v[ 2],v[ 6],v[10],v[14]); \ 170 | G(r,3,v[ 3],v[ 7],v[11],v[15]); \ 171 | G(r,4,v[ 0],v[ 5],v[10],v[15]); \ 172 | G(r,5,v[ 1],v[ 6],v[11],v[12]); \ 173 | G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ 174 | G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ 175 | } while(0) 176 | 177 | static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) 178 | { 179 | uint64_t m[16]; 180 | uint64_t v[16]; 181 | size_t i; 182 | 183 | for( i = 0; i < 16; ++i ) { 184 | m[i] = load64( block + i * sizeof( m[i] ) ); 185 | } 186 | 187 | for( i = 0; i < 8; ++i ) { 188 | v[i] = S->h[i]; 189 | } 190 | 191 | v[ 8] = blake2b_IV[0]; 192 | v[ 9] = blake2b_IV[1]; 193 | v[10] = blake2b_IV[2]; 194 | v[11] = blake2b_IV[3]; 195 | v[12] = blake2b_IV[4] ^ S->t[0]; 196 | v[13] = blake2b_IV[5] ^ S->t[1]; 197 | v[14] = blake2b_IV[6] ^ S->f[0]; 198 | v[15] = blake2b_IV[7] ^ S->f[1]; 199 | 200 | ROUND( 0 ); 201 | ROUND( 1 ); 202 | ROUND( 2 ); 203 | ROUND( 3 ); 204 | ROUND( 4 ); 205 | ROUND( 5 ); 206 | ROUND( 6 ); 207 | ROUND( 7 ); 208 | ROUND( 8 ); 209 | ROUND( 9 ); 210 | ROUND( 10 ); 211 | ROUND( 11 ); 212 | 213 | for( i = 0; i < 8; ++i ) { 214 | S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 215 | } 216 | } 217 | 218 | #undef G 219 | #undef ROUND 220 | 221 | int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) 222 | { 223 | const unsigned char * in = (const unsigned char *)pin; 224 | if( inlen > 0 ) 225 | { 226 | size_t left = S->buflen; 227 | size_t fill = BLAKE2B_BLOCKBYTES - left; 228 | if( inlen > fill ) 229 | { 230 | S->buflen = 0; 231 | memcpy( S->buf + left, in, fill ); /* Fill buffer */ 232 | blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); 233 | blake2b_compress( S, S->buf ); /* Compress */ 234 | in += fill; inlen -= fill; 235 | while(inlen > BLAKE2B_BLOCKBYTES) { 236 | blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); 237 | blake2b_compress( S, in ); 238 | in += BLAKE2B_BLOCKBYTES; 239 | inlen -= BLAKE2B_BLOCKBYTES; 240 | } 241 | } 242 | memcpy( S->buf + S->buflen, in, inlen ); 243 | S->buflen += inlen; 244 | } 245 | return 0; 246 | } 247 | 248 | int blake2b_final( blake2b_state *S, void *out, size_t outlen ) 249 | { 250 | uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; 251 | size_t i; 252 | 253 | if( out == NULL || outlen < S->outlen ) 254 | return -1; 255 | 256 | if( blake2b_is_lastblock( S ) ) 257 | return -1; 258 | 259 | blake2b_increment_counter( S, S->buflen ); 260 | blake2b_set_lastblock( S ); 261 | memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ 262 | blake2b_compress( S, S->buf ); 263 | 264 | for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ 265 | store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); 266 | 267 | memcpy( out, buffer, S->outlen ); 268 | secure_zero_memory(buffer, sizeof(buffer)); 269 | return 0; 270 | } 271 | 272 | /* inlen, at least, should be uint64_t. Others can be size_t. */ 273 | int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) 274 | { 275 | blake2b_state S[1]; 276 | 277 | /* Verify parameters */ 278 | if ( NULL == in && inlen > 0 ) return -1; 279 | 280 | if ( NULL == out ) return -1; 281 | 282 | if( NULL == key && keylen > 0 ) return -1; 283 | 284 | if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; 285 | 286 | if( keylen > BLAKE2B_KEYBYTES ) return -1; 287 | 288 | if( keylen > 0 ) 289 | { 290 | if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; 291 | } 292 | else 293 | { 294 | if( blake2b_init( S, outlen ) < 0 ) return -1; 295 | } 296 | 297 | blake2b_update( S, ( const uint8_t * )in, inlen ); 298 | blake2b_final( S, out, outlen ); 299 | return 0; 300 | } 301 | 302 | int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { 303 | return blake2b(out, outlen, in, inlen, key, keylen); 304 | } 305 | 306 | #if defined(SUPERCOP) 307 | int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) 308 | { 309 | return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); 310 | } 311 | #endif 312 | 313 | #if defined(BLAKE2B_SELFTEST) 314 | #include 315 | #include "blake2-kat.h" 316 | int main( void ) 317 | { 318 | uint8_t key[BLAKE2B_KEYBYTES]; 319 | uint8_t buf[BLAKE2_KAT_LENGTH]; 320 | size_t i, step; 321 | 322 | for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) 323 | key[i] = ( uint8_t )i; 324 | 325 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 326 | buf[i] = ( uint8_t )i; 327 | 328 | /* Test simple API */ 329 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 330 | { 331 | uint8_t hash[BLAKE2B_OUTBYTES]; 332 | blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); 333 | 334 | if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) 335 | { 336 | goto fail; 337 | } 338 | } 339 | 340 | /* Test streaming API */ 341 | for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { 342 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 343 | uint8_t hash[BLAKE2B_OUTBYTES]; 344 | blake2b_state S; 345 | uint8_t * p = buf; 346 | size_t mlen = i; 347 | int err = 0; 348 | 349 | if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { 350 | goto fail; 351 | } 352 | 353 | while (mlen >= step) { 354 | if ( (err = blake2b_update(&S, p, step)) < 0 ) { 355 | goto fail; 356 | } 357 | mlen -= step; 358 | p += step; 359 | } 360 | if ( (err = blake2b_update(&S, p, mlen)) < 0) { 361 | goto fail; 362 | } 363 | if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { 364 | goto fail; 365 | } 366 | 367 | if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { 368 | goto fail; 369 | } 370 | } 371 | } 372 | 373 | puts( "ok" ); 374 | return 0; 375 | fail: 376 | puts("error"); 377 | return -1; 378 | } 379 | #endif 380 | -------------------------------------------------------------------------------- /blake2s-ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "blake2.h" 21 | #include "blake2-impl.h" 22 | 23 | static const uint32_t blake2s_IV[8] = 24 | { 25 | 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, 26 | 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL 27 | }; 28 | 29 | static const uint8_t blake2s_sigma[10][16] = 30 | { 31 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 32 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , 33 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , 34 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , 35 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , 36 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , 37 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , 38 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , 39 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , 40 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , 41 | }; 42 | 43 | static void blake2s_set_lastnode( blake2s_state *S ) 44 | { 45 | S->f[1] = (uint32_t)-1; 46 | } 47 | 48 | /* Some helper functions, not necessarily useful */ 49 | static int blake2s_is_lastblock( const blake2s_state *S ) 50 | { 51 | return S->f[0] != 0; 52 | } 53 | 54 | static void blake2s_set_lastblock( blake2s_state *S ) 55 | { 56 | if( S->last_node ) blake2s_set_lastnode( S ); 57 | 58 | S->f[0] = (uint32_t)-1; 59 | } 60 | 61 | static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) 62 | { 63 | S->t[0] += inc; 64 | S->t[1] += ( S->t[0] < inc ); 65 | } 66 | 67 | static void blake2s_init0( blake2s_state *S ) 68 | { 69 | size_t i; 70 | memset( S, 0, sizeof( blake2s_state ) ); 71 | 72 | for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; 73 | } 74 | 75 | /* init2 xors IV with input parameter block */ 76 | int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) 77 | { 78 | const unsigned char *p = ( const unsigned char * )( P ); 79 | size_t i; 80 | 81 | blake2s_init0( S ); 82 | 83 | /* IV XOR ParamBlock */ 84 | for( i = 0; i < 8; ++i ) 85 | S->h[i] ^= load32( &p[i * 4] ); 86 | 87 | S->outlen = P->digest_length; 88 | return 0; 89 | } 90 | 91 | 92 | /* Sequential blake2s initialization */ 93 | int blake2s_init( blake2s_state *S, size_t outlen ) 94 | { 95 | blake2s_param P[1]; 96 | 97 | /* Move interval verification here? */ 98 | if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; 99 | 100 | P->digest_length = (uint8_t)outlen; 101 | P->key_length = 0; 102 | P->fanout = 1; 103 | P->depth = 1; 104 | store32( &P->leaf_length, 0 ); 105 | store32( &P->node_offset, 0 ); 106 | store16( &P->xof_length, 0 ); 107 | P->node_depth = 0; 108 | P->inner_length = 0; 109 | /* memset(P->reserved, 0, sizeof(P->reserved) ); */ 110 | memset( P->salt, 0, sizeof( P->salt ) ); 111 | memset( P->personal, 0, sizeof( P->personal ) ); 112 | return blake2s_init_param( S, P ); 113 | } 114 | 115 | int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) 116 | { 117 | blake2s_param P[1]; 118 | 119 | if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; 120 | 121 | if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; 122 | 123 | P->digest_length = (uint8_t)outlen; 124 | P->key_length = (uint8_t)keylen; 125 | P->fanout = 1; 126 | P->depth = 1; 127 | store32( &P->leaf_length, 0 ); 128 | store32( &P->node_offset, 0 ); 129 | store16( &P->xof_length, 0 ); 130 | P->node_depth = 0; 131 | P->inner_length = 0; 132 | /* memset(P->reserved, 0, sizeof(P->reserved) ); */ 133 | memset( P->salt, 0, sizeof( P->salt ) ); 134 | memset( P->personal, 0, sizeof( P->personal ) ); 135 | 136 | if( blake2s_init_param( S, P ) < 0 ) return -1; 137 | 138 | { 139 | uint8_t block[BLAKE2S_BLOCKBYTES]; 140 | memset( block, 0, BLAKE2S_BLOCKBYTES ); 141 | memcpy( block, key, keylen ); 142 | blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); 143 | secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ 144 | } 145 | return 0; 146 | } 147 | 148 | #define G(r,i,a,b,c,d) \ 149 | do { \ 150 | a = a + b + m[blake2s_sigma[r][2*i+0]]; \ 151 | d = rotr32(d ^ a, 16); \ 152 | c = c + d; \ 153 | b = rotr32(b ^ c, 12); \ 154 | a = a + b + m[blake2s_sigma[r][2*i+1]]; \ 155 | d = rotr32(d ^ a, 8); \ 156 | c = c + d; \ 157 | b = rotr32(b ^ c, 7); \ 158 | } while(0) 159 | 160 | #define ROUND(r) \ 161 | do { \ 162 | G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ 163 | G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ 164 | G(r,2,v[ 2],v[ 6],v[10],v[14]); \ 165 | G(r,3,v[ 3],v[ 7],v[11],v[15]); \ 166 | G(r,4,v[ 0],v[ 5],v[10],v[15]); \ 167 | G(r,5,v[ 1],v[ 6],v[11],v[12]); \ 168 | G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ 169 | G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ 170 | } while(0) 171 | 172 | static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) 173 | { 174 | uint32_t m[16]; 175 | uint32_t v[16]; 176 | size_t i; 177 | 178 | for( i = 0; i < 16; ++i ) { 179 | m[i] = load32( in + i * sizeof( m[i] ) ); 180 | } 181 | 182 | for( i = 0; i < 8; ++i ) { 183 | v[i] = S->h[i]; 184 | } 185 | 186 | v[ 8] = blake2s_IV[0]; 187 | v[ 9] = blake2s_IV[1]; 188 | v[10] = blake2s_IV[2]; 189 | v[11] = blake2s_IV[3]; 190 | v[12] = S->t[0] ^ blake2s_IV[4]; 191 | v[13] = S->t[1] ^ blake2s_IV[5]; 192 | v[14] = S->f[0] ^ blake2s_IV[6]; 193 | v[15] = S->f[1] ^ blake2s_IV[7]; 194 | 195 | ROUND( 0 ); 196 | ROUND( 1 ); 197 | ROUND( 2 ); 198 | ROUND( 3 ); 199 | ROUND( 4 ); 200 | ROUND( 5 ); 201 | ROUND( 6 ); 202 | ROUND( 7 ); 203 | ROUND( 8 ); 204 | ROUND( 9 ); 205 | 206 | for( i = 0; i < 8; ++i ) { 207 | S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 208 | } 209 | } 210 | 211 | #undef G 212 | #undef ROUND 213 | 214 | int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) 215 | { 216 | const unsigned char * in = (const unsigned char *)pin; 217 | if( inlen > 0 ) 218 | { 219 | size_t left = S->buflen; 220 | size_t fill = BLAKE2S_BLOCKBYTES - left; 221 | if( inlen > fill ) 222 | { 223 | S->buflen = 0; 224 | memcpy( S->buf + left, in, fill ); /* Fill buffer */ 225 | blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); 226 | blake2s_compress( S, S->buf ); /* Compress */ 227 | in += fill; inlen -= fill; 228 | while(inlen > BLAKE2S_BLOCKBYTES) { 229 | blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); 230 | blake2s_compress( S, in ); 231 | in += BLAKE2S_BLOCKBYTES; 232 | inlen -= BLAKE2S_BLOCKBYTES; 233 | } 234 | } 235 | memcpy( S->buf + S->buflen, in, inlen ); 236 | S->buflen += inlen; 237 | } 238 | return 0; 239 | } 240 | 241 | int blake2s_final( blake2s_state *S, void *out, size_t outlen ) 242 | { 243 | uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; 244 | size_t i; 245 | 246 | if( out == NULL || outlen < S->outlen ) 247 | return -1; 248 | 249 | if( blake2s_is_lastblock( S ) ) 250 | return -1; 251 | 252 | blake2s_increment_counter( S, ( uint32_t )S->buflen ); 253 | blake2s_set_lastblock( S ); 254 | memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ 255 | blake2s_compress( S, S->buf ); 256 | 257 | for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ 258 | store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); 259 | 260 | memcpy( out, buffer, outlen ); 261 | secure_zero_memory(buffer, sizeof(buffer)); 262 | return 0; 263 | } 264 | 265 | int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) 266 | { 267 | blake2s_state S[1]; 268 | 269 | /* Verify parameters */ 270 | if ( NULL == in && inlen > 0 ) return -1; 271 | 272 | if ( NULL == out ) return -1; 273 | 274 | if ( NULL == key && keylen > 0) return -1; 275 | 276 | if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; 277 | 278 | if( keylen > BLAKE2S_KEYBYTES ) return -1; 279 | 280 | if( keylen > 0 ) 281 | { 282 | if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; 283 | } 284 | else 285 | { 286 | if( blake2s_init( S, outlen ) < 0 ) return -1; 287 | } 288 | 289 | blake2s_update( S, ( const uint8_t * )in, inlen ); 290 | blake2s_final( S, out, outlen ); 291 | return 0; 292 | } 293 | 294 | #if defined(SUPERCOP) 295 | int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) 296 | { 297 | return blake2s( out, BLAKE2S_OUTBYTES in, inlen, NULL, 0 ); 298 | } 299 | #endif 300 | 301 | #if defined(BLAKE2S_SELFTEST) 302 | #include 303 | #include "blake2-kat.h" 304 | int main( void ) 305 | { 306 | uint8_t key[BLAKE2S_KEYBYTES]; 307 | uint8_t buf[BLAKE2_KAT_LENGTH]; 308 | size_t i, step; 309 | 310 | for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) 311 | key[i] = ( uint8_t )i; 312 | 313 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 314 | buf[i] = ( uint8_t )i; 315 | 316 | /* Test simple API */ 317 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 318 | { 319 | uint8_t hash[BLAKE2S_OUTBYTES]; 320 | blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); 321 | 322 | if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) 323 | { 324 | goto fail; 325 | } 326 | } 327 | 328 | /* Test streaming API */ 329 | for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { 330 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 331 | uint8_t hash[BLAKE2S_OUTBYTES]; 332 | blake2s_state S; 333 | uint8_t * p = buf; 334 | size_t mlen = i; 335 | int err = 0; 336 | 337 | if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { 338 | goto fail; 339 | } 340 | 341 | while (mlen >= step) { 342 | if ( (err = blake2s_update(&S, p, step)) < 0 ) { 343 | goto fail; 344 | } 345 | mlen -= step; 346 | p += step; 347 | } 348 | if ( (err = blake2s_update(&S, p, mlen)) < 0) { 349 | goto fail; 350 | } 351 | if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { 352 | goto fail; 353 | } 354 | 355 | if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { 356 | goto fail; 357 | } 358 | } 359 | } 360 | 361 | puts( "ok" ); 362 | return 0; 363 | fail: 364 | puts("error"); 365 | return -1; 366 | } 367 | #endif 368 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | PHP_ARG_ENABLE(blake2, 2 | [Whether to enable BLAKE2 support], 3 | [--enable-blake2 Enable BLAKE2 Extension]) 4 | 5 | if test "$PHP_BLAKE2" != "no"; then 6 | PHP_NEW_EXTENSION(blake2, php_blake2.c blake2b-ref.c blake2s-ref.c, $ext_shared) 7 | fi -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | ARG_ENABLE('blake2', 'blake2 support', 'no'); 2 | 3 | if (PHP_BLAKE2 != 'no') { 4 | AC_DEFINE('HAVE_BLAKE2', 1, 'blake2 support enabled'); 5 | 6 | EXTENSION('blake2', 'php_blake2.c', true, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); 7 | ADD_SOURCES(configure_module_dirname, 'blake2b-ref.c blake2s-ref.c', 'blake2'); 8 | } 9 | -------------------------------------------------------------------------------- /php_blake2.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include "php.h" 6 | #include "ext/standard/info.h" 7 | #include "ext/hash/php_hash.h" 8 | #include "blake2.h" 9 | #include "php_blake2.h" 10 | 11 | #define PHP_BLAKE2_NAME "BLAKE2" 12 | #define PHP_BLAKE2_VERSION "0.1.0" 13 | 14 | zend_function_entry blake2_functions[] = { 15 | PHP_FE(blake2, NULL) 16 | PHP_FALIAS(blake2b, blake2, NULL) 17 | PHP_FE(blake2s, NULL) 18 | PHP_FE(blake2_file, NULL) 19 | PHP_FALIAS(b2sum, blake2_file, NULL) 20 | {NULL, NULL, NULL} 21 | }; 22 | 23 | zend_module_entry blake2_module_entry = { 24 | #if ZEND_MODULE_API_NO >= 20010901 25 | STANDARD_MODULE_HEADER, 26 | #endif 27 | PHP_BLAKE2_NAME, 28 | blake2_functions, 29 | NULL, 30 | NULL, 31 | NULL, 32 | NULL, 33 | NULL, 34 | #if ZEND_MODULE_API_NO >= 20010901 35 | PHP_BLAKE2_VERSION, 36 | #endif 37 | STANDARD_MODULE_PROPERTIES 38 | }; 39 | 40 | #ifdef COMPILE_DL_BLAKE2 41 | ZEND_GET_MODULE(blake2) 42 | #endif 43 | 44 | PHP_FUNCTION(blake2) 45 | { 46 | #if ZEND_MODULE_API_NO >= 20151012 47 | zend_long hashByteLength = BLAKE2B_OUTBYTES; 48 | size_t dataByteLength; 49 | size_t keyLength = 0; 50 | #else 51 | long hashByteLength = BLAKE2B_OUTBYTES; 52 | int dataByteLength; 53 | int keyLength = 0; 54 | #endif 55 | unsigned char *data; 56 | unsigned char *key; 57 | zend_bool rawOutput = 0; 58 | 59 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lsb", &data, &dataByteLength, &hashByteLength, &key, &keyLength, &rawOutput) == FAILURE) { 60 | return; 61 | } 62 | 63 | zend_bool hasError = 0; 64 | 65 | if (hashByteLength < 1) { 66 | hasError = 1; 67 | zend_error(E_WARNING, "BLAKE2 output length is too short"); 68 | } else if (hashByteLength > BLAKE2B_OUTBYTES) { 69 | hasError = 1; 70 | zend_error(E_WARNING, "BLAKE2 output length is too long"); 71 | } 72 | 73 | if (keyLength > BLAKE2B_KEYBYTES) { 74 | hasError = 1; 75 | zend_error(E_WARNING, "BLAKE2 key length is too long"); 76 | } 77 | 78 | if (hasError) { 79 | RETURN_FALSE; 80 | } 81 | 82 | char* hashOutput = (char*) emalloc(hashByteLength); 83 | 84 | int result = blake2b(hashOutput, hashByteLength, data, dataByteLength, key, keyLength); 85 | 86 | if (result != 0) { 87 | zend_error(E_WARNING, "Error generating BLAKE2 hash"); 88 | RETURN_FALSE; 89 | } 90 | 91 | if (rawOutput) { 92 | #if ZEND_MODULE_API_NO >= 20151012 93 | RETURN_STRINGL(hashOutput, hashByteLength); 94 | #else 95 | RETURN_STRINGL(hashOutput, hashByteLength, 1); 96 | #endif 97 | } else { 98 | char* hex = (char*) emalloc(hashByteLength * 2 + 1); 99 | php_hash_bin2hex(hex, (unsigned char *) hashOutput, hashByteLength); 100 | hex[hashByteLength * 2] = '\0'; 101 | 102 | #if ZEND_MODULE_API_NO >= 20151012 103 | RETURN_STRING(hex); 104 | #else 105 | RETURN_STRING(hex, 1); 106 | #endif 107 | 108 | efree(hex); 109 | } 110 | 111 | efree(hashOutput); 112 | } 113 | 114 | PHP_FUNCTION(blake2s) 115 | { 116 | #if ZEND_MODULE_API_NO >= 20151012 117 | zend_long hashByteLength = BLAKE2S_OUTBYTES; 118 | size_t dataByteLength; 119 | size_t keyLength = 0; 120 | #else 121 | long hashByteLength = BLAKE2S_OUTBYTES; 122 | int dataByteLength; 123 | int keyLength = 0; 124 | #endif 125 | unsigned char *data; 126 | unsigned char *key; 127 | zend_bool rawOutput = 0; 128 | 129 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lsb", &data, &dataByteLength, &hashByteLength, &key, &keyLength, &rawOutput) == FAILURE) { 130 | return; 131 | } 132 | 133 | zend_bool hasError = 0; 134 | 135 | if (hashByteLength < 1) { 136 | hasError = 1; 137 | zend_error(E_WARNING, "BLAKE2s output length is too short"); 138 | } else if (hashByteLength > BLAKE2S_OUTBYTES) { 139 | hasError = 1; 140 | zend_error(E_WARNING, "BLAKE2s output length is too long"); 141 | } 142 | 143 | if (keyLength > BLAKE2S_KEYBYTES) { 144 | hasError = 1; 145 | zend_error(E_WARNING, "BLAKE2s key length is too long"); 146 | } 147 | 148 | if (hasError) { 149 | RETURN_FALSE; 150 | } 151 | 152 | char* hashOutput = (char*) emalloc(hashByteLength); 153 | 154 | int result = blake2s(hashOutput, hashByteLength, data, dataByteLength, key, keyLength); 155 | 156 | if (result != 0) { 157 | zend_error(E_WARNING, "Error generating BLAKE2s hash"); 158 | RETURN_FALSE; 159 | } 160 | 161 | if (rawOutput) { 162 | #if ZEND_MODULE_API_NO >= 20151012 163 | RETURN_STRINGL(hashOutput, hashByteLength); 164 | #else 165 | RETURN_STRINGL(hashOutput, hashByteLength, 1); 166 | #endif 167 | } else { 168 | char* hex = (char*) emalloc(hashByteLength * 2 + 1); 169 | php_hash_bin2hex(hex, (unsigned char *) hashOutput, hashByteLength); 170 | hex[hashByteLength * 2] = '\0'; 171 | 172 | #if ZEND_MODULE_API_NO >= 20151012 173 | RETURN_STRING(hex); 174 | #else 175 | RETURN_STRING(hex, 1); 176 | #endif 177 | 178 | efree(hex); 179 | } 180 | 181 | efree(hashOutput); 182 | } 183 | 184 | PHP_FUNCTION(blake2_file) 185 | { 186 | #if ZEND_MODULE_API_NO >= 20151012 187 | zend_long hashByteLength = BLAKE2B_OUTBYTES; 188 | size_t dataByteLength; 189 | #else 190 | long hashByteLength = BLAKE2B_OUTBYTES; 191 | int dataByteLength; 192 | #endif 193 | 194 | char *data; 195 | int rawOutput = 0; 196 | 197 | php_stream *stream; 198 | int n; 199 | unsigned char buf[1024]; 200 | 201 | blake2b_state S[1]; 202 | 203 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|b", &data, &dataByteLength, &rawOutput) == FAILURE) { 204 | return; 205 | } 206 | 207 | stream = php_stream_open_wrapper(data, "rb", REPORT_ERRORS, NULL); 208 | if (!stream) { 209 | RETURN_FALSE; 210 | } 211 | 212 | char* hashOutput = (char*) emalloc(hashByteLength); 213 | 214 | blake2b_init(S, hashByteLength); 215 | 216 | while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { 217 | blake2b_update(S, (const uint8_t *)buf, n); 218 | } 219 | 220 | blake2b_final(S, hashOutput, hashByteLength); 221 | 222 | php_stream_close(stream); 223 | 224 | if (n<0) { 225 | RETURN_FALSE; 226 | } 227 | 228 | if (rawOutput) { 229 | #if ZEND_MODULE_API_NO >= 20151012 230 | RETURN_STRINGL(hashOutput, hashByteLength); 231 | #else 232 | RETURN_STRINGL(hashOutput, hashByteLength, 1); 233 | #endif 234 | } else { 235 | char* hex = (char*) emalloc(hashByteLength * 2 + 1); 236 | php_hash_bin2hex(hex, (unsigned char *) hashOutput, hashByteLength); 237 | hex[hashByteLength * 2] = '\0'; 238 | #if ZEND_MODULE_API_NO >= 20151012 239 | RETURN_STRING(hex); 240 | #else 241 | RETURN_STRING(hex, 1); 242 | #endif 243 | efree(hex); 244 | } 245 | 246 | efree(hashOutput); 247 | } 248 | -------------------------------------------------------------------------------- /php_blake2.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_BLAKE2_H 2 | #define PHP_BLAKE2_H 3 | 4 | PHP_FUNCTION(blake2); 5 | PHP_FUNCTION(blake2s); 6 | PHP_FUNCTION(blake2_file); 7 | 8 | extern zend_module_entry blake2_module_entry; 9 | #define phpext_blake2_ptr &blake2_module_entry 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /tests/b2sum.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify the b2sum is an alias to blake2_file and have the same output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | a61b779ff667fbcc4775cbb02cd0763b9b5312fe6359a44a003f582ce6897c81a38a876122ce91dfec547d582fe269f6ea9bd291b60bccf95006dac10a4316f2 11 | -------------------------------------------------------------------------------- /tests/blake2_error.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify BLAKE2 error output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 12 | --EXPECTF-- 13 | Warning: BLAKE2 output length is too short in %s on line %d 14 | 15 | Warning: BLAKE2 output length is too long in %s on line %d 16 | 17 | Warning: BLAKE2 key length is too long in %s on line %d 18 | 19 | Warning: BLAKE2 output length is too long in %s on line %d 20 | 21 | Warning: BLAKE2 key length is too long in %s on line %d 22 | -------------------------------------------------------------------------------- /tests/blake2_file.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify blake2_file output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | a61b779ff667fbcc4775cbb02cd0763b9b5312fe6359a44a003f582ce6897c81a38a876122ce91dfec547d582fe269f6ea9bd291b60bccf95006dac10a4316f2 11 | -------------------------------------------------------------------------------- /tests/blake2_hash.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify BLAKE2 output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 11 | --EXPECT-- 12 | 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce 13 | 6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183 14 | 5b4bbc84b59ab5d9146089b143fd52f38638dcac -------------------------------------------------------------------------------- /tests/blake2b_hash.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify the blake2b is an alias to blake2 and have the same output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 14 | --EXPECT-- 15 | 4386a08a265111c9896f56456e2cb61a64239115c4784cf438e36cc851221972da3fb0115f73cd02486254001f878ab1fd126aac69844ef1c1ca152379d0a9bd 16 | -------------------------------------------------------------------------------- /tests/blake2s_hash.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify BLAKE2s output 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 11 | --EXPECT-- 12 | 69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9 13 | 619a15b0f4dd21ef4bd626a9146af64561caf1325b21bccf755e4d7fbc31a65f 14 | 766745fb94ffb0512e0a544a17860f3f2484c4af -------------------------------------------------------------------------------- /tests/sample.txt: -------------------------------------------------------------------------------- 1 | 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 --------------------------------------------------------------------------------