├── README.txt ├── bench_blake2.py ├── blake2.py └── demo_blake2.py /README.txt: -------------------------------------------------------------------------------- 1 | 2 | blake2.py -- version 1 3 | 4 | This pure Python implementation of BLAKE2 supports both 5 | BLAKE2b and BLAKE2s. It runs under both Python 2.7 and 6 | Python 3.3. For information about the BLAKE2 algorithm 7 | please see https://blake2.net 8 | 9 | blake2.py differs from the structure of the C reference 10 | version primarily by effecting classes (object orientation) 11 | and certain optimizations in the area of compress() and 12 | G(). The goal of these optimizations was to improve 13 | performance, and while a gain of about 50% was attained, 14 | this program is way too slow to be competitive with C 15 | implementations. This program can be useful, however, 16 | where a pure Python implementation is required, where the 17 | data to be hashed is small, and, of course, for educational 18 | purposes. 19 | 20 | Credit is given to Dmitry Chestnykh 21 | for his work defining a Python API for pyblake2 and his 22 | excellent documentation of that interface. Please see 23 | http://pythonhosted.org/pyblake2/ 24 | 25 | I have adoped much of Dmitry's API and many of his data 26 | names. Here are a few known differences between blake2.py 27 | and pyblake2. 28 | 29 | - capitalization of BLAKE2b and BLAKE2s class names 30 | 31 | - digest() is an alias for final() 32 | 33 | - under Python 2.7 the returned digest is a str; under 34 | Python 3.3 the digest is a bytes object. Neither is 35 | a pyblake2 hash object. 36 | 37 | - hexdigest() returns a str under Pythons 2.7 and 3.3 38 | 39 | - http://pythonhosted.org/pyblake2/module.html#pyblake2.hash.digest 40 | says "Return the digest of the data so far" which 41 | implies hashing may be resumed after a digest is 42 | retrieved at some arbitrary interim point. What really 43 | happens is that when final() is called, padding is 44 | added (if needed), and a final compress is performed on 45 | the last block. An intermediate digest value will NOT 46 | include a residual value left in the buffer (unless you 47 | just happen to be on an exact multiple of BLOCKSIZE). 48 | IMO, to resume hashing after "closing out" the state 49 | should NOT be permitted, hence blake2.py will throw an 50 | exception if resumption is attempted. 51 | 52 | Arguably, a better approach would be to make a deepcopy 53 | by calling copy() and then call digest() or hexdigest() 54 | on the copy, and resume hashing on the original. 55 | 56 | - digest() and hexdigest() may be called multiple times 57 | when finished hashing. Each calls final(), but final() 58 | performs the final compression only once. 59 | 60 | 61 | Other notes: 62 | 63 | - All data, key, salt, and person inputs are big endian 64 | bytes, NOT strings. Likewise, the final digest is 65 | big endian bytes. 66 | 67 | - blake2.py has been tested in sequential and tree modes; 68 | it has NOT been tested in parallel mode. 69 | 70 | - blake2.py is NOT a secure implementation. For example, 71 | keys are not securely overwritten after use. Use this 72 | implementation on a presumably secure platform only. 73 | 74 | 75 | Simple usage example: 76 | 77 | import blake2 78 | 79 | digest = blake2.BLAKE2b(b'hello world').digest() 80 | 81 | 82 | Another, generating a 20-byte digest (in hex): 83 | 84 | from blake2 import BLAKE2b 85 | 86 | data1 = b'hello ' 87 | data2 = b'world' 88 | b2 = BLAKE2b(digest_size=20) 89 | b2.update(data1) 90 | b2.update(data2) 91 | hexdigest = b2.hexdigest() 92 | 93 | 94 | ----- 95 | 96 | 97 | "miniServer" BLAKE2b thruput (OSX 2.53GHz Core2Duo): 98 | MiB/sec 99 | 0.22 python 2.7, blake2.py v1 100 | 0.36 python 3.3, blake2.py v1 101 | 0.60 pypy 2.2 (python 2.7), blake2.py v1 102 | 103 | 0.35 python 2.7, cythonized blake2.py v1 104 | 0.59 python 3.3, cythonized blake2.py v1 105 | 1.99 python 2.7, cython + some cdef unsigned long long 106 | 107 | >>> 301.01 python 2.7, Dmitry's pyblake2 wrapper <<< 108 | 109 | "godspeed" BLAKE2b thruput (Ubuntu 12.4LTS 4.4GHz i5-3570K): 110 | MiB/sec 111 | 0.72 python 2.7, blake2.py v1 112 | 0.84 python 3.2, blake2.py v1 113 | 0.96 pypy 2.2 (python 2.7), blake2.py v1 114 | 115 | ...still [very] slow. :-/ 116 | 117 | Consider using blake2.py when you need a pure Python 118 | implementation, and convert to Dmitry's pyblake2, with 119 | hopefully minimum effort, when more speed is required. 120 | 121 | ----- 122 | 123 | License: 124 | 125 | Copyright (c) 2009-2018 Larry Bugbee, Kent, WA, USA 126 | 127 | Permission to use, copy, modify, and/or distribute this software 128 | for any purpose with or without fee is hereby granted, provided 129 | that the above copyright notice and this permission notice appear 130 | in all copies. 131 | 132 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 133 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 134 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 135 | THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 136 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 137 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 138 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 139 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 140 | 141 | (the ISC license, a minor tweak of the BSD license) 142 | 143 | Enjoy, 144 | 145 | Larry Bugbee 146 | December 2013 147 | rev Mar 2014, Mar 2018 148 | 149 | 150 | PS: If you are interested in a 100% Python implementation of the 151 | original BLAKE hash algorithm, predecessor to BLAKE2, submitted 152 | to and a finalist in the NIST SHA3 competition, go to: 153 | http://www.seanet.com/~bugbee/crypto/blake/ 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /bench_blake2.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | doc = """ 4 | 5 | bench_blake2.py -- version 1 6 | 7 | see blake2.py's README for more information. 8 | 9 | Enjoy, 10 | 11 | Larry Bugbee 12 | December 2013 13 | 14 | """ 15 | 16 | import time, binascii, platform 17 | import blake2 18 | 19 | #----------------------------------------------------------------------- 20 | 21 | def bench2b(loops=1): 22 | mib = 2**20 23 | numbytes = mib 24 | text = b' '*numbytes 25 | 26 | print('') 27 | print('BLAKE2b hashing %s MiB' % (loops)) 28 | 29 | mibs = mib * loops 30 | t0 = time.time() 31 | b2 = blake2.BLAKE2b() 32 | for i in range(loops): 33 | b2.update(text) 34 | digest = b2.final() 35 | t1 = time.time() 36 | 37 | print(' ??? %s' % binascii.hexlify(digest).decode()) 38 | 39 | elapsed = t1-t0 40 | print(' time for %d MiB hashed: %.3f seconds' % (loops, elapsed)) 41 | print(' hashed %.2f MiB per second' % (mibs/mib/elapsed)) 42 | 43 | #----------------------------------------------------------------------- 44 | 45 | def bench2s(loops=1): 46 | mb = 1000 * 1000 47 | text = b' '*mb 48 | 49 | print('') 50 | print('BLAKE2s hashing %s MB' % (loops)) 51 | 52 | t0 = time.time() 53 | b2 = blake2.BLAKE2s() 54 | for i in range(loops): 55 | b2.update(text) 56 | digest = b2.final() 57 | t1 = time.time() 58 | 59 | print(' ??? %s' % binascii.hexlify(digest).decode()) 60 | 61 | elapsed = t1-t0 62 | print(' time for %d MB hashed: %.3f seconds' % (loops, elapsed)) 63 | 64 | #----------------------------------------------------------------------- 65 | #----------------------------------------------------------------------- 66 | 67 | if __name__ == '__main__': 68 | 69 | print('') 70 | print(platform.python_version()) 71 | 72 | bench2b(1) 73 | 74 | if 0: 75 | bench2b(25) 76 | 77 | if 0: 78 | bench2s(1) 79 | 80 | print 81 | 82 | #----------------------------------------------------------------------- 83 | #----------------------------------------------------------------------- 84 | #----------------------------------------------------------------------- 85 | -------------------------------------------------------------------------------- /blake2.py: -------------------------------------------------------------------------------- 1 | 2 | # encoding: utf-8 3 | 4 | doc = """ 5 | 6 | blake2.py -- version 1 7 | 8 | This 100% Python implementation of BLAKE2 supports both 9 | BLAKE2b and BLAKE2s. It runs under both Python 2.7 and 10 | Python 3.3. 11 | 12 | https://blake2.net 13 | 14 | ----- 15 | 16 | License: 17 | 18 | Copyright (c) 2009-2018 Larry Bugbee, Kent, WA, USA 19 | 20 | Permission to use, copy, modify, and/or distribute this software 21 | for any purpose with or without fee is hereby granted, provided 22 | that the above copyright notice and this permission notice appear 23 | in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 26 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 27 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 28 | THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 29 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 30 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 31 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 32 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 33 | 34 | (the ISC license, a minor tweak of the BSD license) 35 | 36 | Enjoy, 37 | 38 | Larry Bugbee 39 | December 2013 40 | rev Mar 2018 41 | 42 | """ 43 | 44 | import struct, binascii, copy 45 | from ctypes import * 46 | 47 | 48 | DBUG = True # True False 49 | DBUG2 = False # True False 50 | 51 | if DBUG: 52 | print('') 53 | print(' *** this is a beta version of blake2.py ***') 54 | print(' *** look for a final version in coming weeks ***') 55 | 56 | 57 | MASK8BITS = 0xff 58 | MASK16BITS = 0xffff 59 | MASK32BITS = 0xffffffff 60 | MASK48BITS = 0xffffffffffff 61 | MASK64BITS = 0xffffffffffffffff 62 | 63 | 64 | #--------------------------------------------------------------- 65 | 66 | class BLAKE2(object): 67 | """ BLAKE2 is a base class for BLAKE2b and BLAKE2s """ 68 | 69 | sigma = [ 70 | [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 ], 71 | [ 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3 ], 72 | [ 11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4 ], 73 | [ 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8 ], 74 | [ 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13 ], 75 | [ 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9 ], 76 | [ 12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11 ], 77 | [ 13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10 ], 78 | [ 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5 ], 79 | [ 10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13 ,0 ], 80 | [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 ], 81 | [ 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3 ] 82 | ] # only 1st 10 rows are used by BLAKE2s 83 | 84 | 85 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 86 | 87 | def __init__(self, digest_size=0, **args): 88 | print(""" 89 | *********************************************** 90 | * You just instantiated a base class. Please * 91 | * instantiate either BLAKE2b or BLAKE2s. * 92 | *********************************************** 93 | """) 94 | raise Exception('base class instantiation') 95 | 96 | 97 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 98 | 99 | def _init(self, key=b''): 100 | 101 | assert len(key) <= self.KEYBYTES 102 | 103 | # load parameters 104 | P = self.PARAMS() 105 | P.F.digest_size = self.digest_size 106 | P.F.key_length = len(key) 107 | P.F.fanout = self.fanout 108 | P.F.depth = self.depth 109 | P.F.leaf_size = self.leaf_size 110 | P.F.node_offset_lo = self.node_offset & MASK32BITS 111 | P.F.node_offset_hi = self.node_offset >> 32 112 | P.F.node_depth = self.node_depth 113 | P.F.inner_size = self.inner_size 114 | # P.F.reserved is not defined in BLAKE2s so we cannot init it 115 | # to zeros for both BLAKE2s and BLAKE2b here. Fortunately ctypes 116 | # initializes to zeros so we don't have to. :-)) 117 | # P.F.reserved = chr(0) * 14 118 | P.F.salt = (self.salt + 119 | (chr(0).encode())*(self.SALTBYTES-len(self.salt))) 120 | P.F.person = (self.person + 121 | (chr(0).encode())*(self.PERSONALBYTES-len(self.person))) 122 | 123 | 124 | if DBUG2: 125 | print('') 126 | fmt = '%0' + str(self.WORDBYTES*2) + 'x' 127 | for i in range(8): 128 | print(' %2d: %s' % (i*8, fmt % P.W[i])) 129 | 130 | 131 | self.h = [self.IV[i] ^ P.W[i] for i in range(8)] 132 | 133 | self.totbytes = 0 134 | self.t = [0]*2 135 | self.f = [0]*2 136 | self.buflen = 0 137 | self.buf = b'' 138 | self.finalized = False 139 | self.block_size = self.BLOCKBYTES 140 | 141 | if key: 142 | block = key + (chr(0).encode())*(self.BLOCKBYTES-len(key)) 143 | self.update(block) 144 | 145 | if self.data: 146 | self.update(self.data) 147 | 148 | 149 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 150 | 151 | def _compress(self, block): 152 | 153 | # Dereference these for [very small] speed improvement. 154 | # Perhaps more than anything, this makes the code 155 | # easier to read. 156 | MASKBITS = self.MASKBITS 157 | WORDBITS = self.WORDBITS 158 | WORDBYTES = self.WORDBYTES 159 | IV = self.IV 160 | sigma = self.sigma 161 | ROT1 = self.ROT1 162 | ROT2 = self.ROT2 163 | ROT3 = self.ROT3 164 | ROT4 = self.ROT4 165 | WB_ROT1 = WORDBITS - ROT1 166 | WB_ROT2 = WORDBITS - ROT2 167 | WB_ROT3 = WORDBITS - ROT3 168 | WB_ROT4 = WORDBITS - ROT4 169 | 170 | # convert block (bytes) into 16 LE words 171 | m = struct.unpack_from('<16%s' % self.WORDFMT, bytes(block)) 172 | 173 | v = [0]*16 174 | v[ 0: 8] = self.h 175 | v[ 8:12] = IV[:4] 176 | v[12] = self.t[0] ^ IV[4] 177 | v[13] = self.t[1] ^ IV[5] 178 | v[14] = self.f[0] ^ IV[6] 179 | v[15] = self.f[1] ^ IV[7] 180 | 181 | # Within the confines of the Python language, this is a 182 | # highly optimized version of G(). It differs some from 183 | # the formal specification and reference implementation. 184 | def G(a, b, c, d): 185 | # dereference v[] for another small speed improvement 186 | va = v[a] 187 | vb = v[b] 188 | vc = v[c] 189 | vd = v[d] 190 | va = (va + vb + msri2) & MASKBITS 191 | w = vd ^ va 192 | vd = (w >> ROT1) | (w << (WB_ROT1)) & MASKBITS 193 | vc = (vc + vd) & MASKBITS 194 | w = vb ^ vc 195 | vb = (w >> ROT2) | (w << (WB_ROT2)) & MASKBITS 196 | va = (va + vb + msri21) & MASKBITS 197 | w = vd ^ va 198 | vd = (w >> ROT3) | (w << (WB_ROT3)) & MASKBITS 199 | vc = (vc + vd) & MASKBITS 200 | w = vb ^ vc 201 | vb = (w >> ROT4) | (w << (WB_ROT4)) & MASKBITS 202 | # re-reference v[] 203 | v[a] = va 204 | v[b] = vb 205 | v[c] = vc 206 | v[d] = vd 207 | 208 | # time to ChaCha 209 | for r in range(self.ROUNDS): 210 | # resolve as much as possible outside G() and 211 | # don't pass as argument, let scope do its job. 212 | # Result is a 50% speed increase, but sadly, 213 | # "slow" divided by 1.5 is still "slow". :-/ 214 | sr = sigma[r] 215 | msri2 = m[sr[0]] 216 | msri21 = m[sr[1]] 217 | G( 0, 4, 8, 12) 218 | msri2 = m[sr[2]] 219 | msri21 = m[sr[3]] 220 | G( 1, 5, 9, 13) 221 | msri2 = m[sr[4]] 222 | msri21 = m[sr[5]] 223 | G( 2, 6, 10, 14) 224 | msri2 = m[sr[6]] 225 | msri21 = m[sr[7]] 226 | G( 3, 7, 11, 15) 227 | msri2 = m[sr[8]] 228 | msri21 = m[sr[9]] 229 | G( 0, 5, 10, 15) 230 | msri2 = m[sr[10]] 231 | msri21 = m[sr[11]] 232 | G( 1, 6, 11, 12) 233 | msri2 = m[sr[12]] 234 | msri21 = m[sr[13]] 235 | G( 2, 7, 8, 13) 236 | msri2 = m[sr[14]] 237 | msri21 = m[sr[15]] 238 | G( 3, 4, 9, 14) 239 | 240 | self.h = [self.h[i] ^ v[i] ^ v[i+8] for i in range(8)] 241 | 242 | 243 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 244 | 245 | def update(self, data): 246 | 247 | assert self.finalized == False 248 | 249 | BLOCKBYTES = self.BLOCKBYTES 250 | 251 | datalen = len(data) 252 | dataptr = 0 253 | while True: 254 | if len(self.buf) > BLOCKBYTES: 255 | self._increment_counter(BLOCKBYTES) 256 | self._compress(self.buf[:BLOCKBYTES]) 257 | self.buf = self.buf[BLOCKBYTES:] 258 | if dataptr < datalen: 259 | self.buf += data[dataptr:dataptr + BLOCKBYTES] 260 | dataptr += BLOCKBYTES 261 | else: 262 | break 263 | 264 | 265 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 266 | 267 | def final(self): 268 | # is there any residue remaining to be processed? 269 | if not self.finalized and len(self.buf): 270 | self._increment_counter(len(self.buf)) 271 | self._set_lastblock() 272 | # add padding 273 | self.buf += (chr(0).encode())*(self.BLOCKBYTES - len(self.buf)) 274 | # final compress 275 | self._compress(self.buf) 276 | self.buf = b'' # nothing more (no residue) 277 | # convert 8 LE words into digest (bytestring) 278 | self.digest_ = struct.pack('<8%s' % self.WORDFMT, *tuple(self.h)) 279 | self.finalized = True 280 | return self.digest_[:self.digest_size] 281 | 282 | digest = final 283 | 284 | def hexdigest(self): 285 | return binascii.hexlify(self.final()).decode() 286 | 287 | 288 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 289 | # f0 = 0 if NOT last block, 0xffffffff... if last block 290 | # f1 = 0 if sequential mode or (tree mode and NOT last 291 | # node), 0xffffffff... if tree mode AND last node 292 | 293 | def _set_lastblock(self): 294 | if self.last_node: 295 | self.f[1] = self.MASKBITS 296 | self.f[0] = self.MASKBITS 297 | 298 | def _increment_counter(self, numbytes): 299 | self.totbytes += numbytes 300 | self.t[0] = self.totbytes & self.MASKBITS 301 | self.t[1] = self.totbytes >> self.WORDBITS 302 | 303 | 304 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 305 | # common utility functions 306 | 307 | def copy(self): 308 | return copy.deepcopy(self) 309 | 310 | 311 | #--------------------------------------------------------------- 312 | 313 | class BLAKE2b(BLAKE2): 314 | 315 | WORDBITS = 64 316 | WORDBYTES = 8 317 | MASKBITS = MASK64BITS 318 | WORDFMT = 'Q' # used in _compress() and final() 319 | 320 | ROUNDS = 12 321 | BLOCKBYTES = 128 322 | OUTBYTES = 64 323 | KEYBYTES = 64 324 | SALTBYTES = 16 # see also hardcoded value in ParamFields64 325 | PERSONALBYTES = 16 # see also hardcoded value in ParamFields64 326 | 327 | IV = [ 328 | 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 329 | 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 330 | 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 331 | 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 332 | ] 333 | 334 | ROT1 = 32 335 | ROT2 = 24 336 | ROT3 = 16 337 | ROT4 = 63 338 | 339 | 340 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 341 | 342 | def __init__(self, data=b'', digest_size=64, key=b'', 343 | salt=b'', person=b'', fanout=1, depth=1, 344 | leaf_size=0, node_offset=0, node_depth=0, 345 | inner_size=0, last_node=False): 346 | 347 | assert 1 <= digest_size <= self.OUTBYTES 348 | assert len(key) <= self.KEYBYTES 349 | assert len(salt) <= self.SALTBYTES 350 | assert len(person) <= self.PERSONALBYTES 351 | assert 0 <= fanout <= MASK8BITS 352 | assert 0 <= depth <= MASK8BITS 353 | assert 0 <= leaf_size <= MASK32BITS 354 | assert 0 <= node_offset <= MASK64BITS 355 | assert 0 <= node_depth <= MASK8BITS 356 | assert 0 <= inner_size <= MASK8BITS 357 | 358 | # - - - - - - - - - - - - - - - - - - - - - - - - - 359 | # use ctypes LittleEndianStructure and Union as a 360 | # convenient way to organize complex structs, convert 361 | # to little endian, and access by words 362 | class ParamFields64(LittleEndianStructure): 363 | _fields_ = [("digest_size", c_ubyte), 364 | ("key_length", c_ubyte), 365 | ("fanout", c_ubyte), 366 | ("depth", c_ubyte), 367 | ("leaf_size", c_uint32), 368 | ("node_offset_lo", c_uint32), 369 | ("node_offset_hi", c_uint32), 370 | ("node_depth", c_ubyte), 371 | ("inner_size", c_ubyte), 372 | ("reserved", c_char * 14), 373 | ("salt", c_char * 16), 374 | ("person", c_char * 16), 375 | ] 376 | 377 | class Params64(Union): 378 | _fields_ = [("F", ParamFields64), 379 | ("W", c_uint64 * 8), 380 | ] 381 | 382 | # this next makes PARAMS a 'proper' instance variable 383 | self.PARAMS = Params64 384 | 385 | # key is passed as an argument; all other variables are 386 | # defined as instance variables 387 | self.digest_size = digest_size 388 | self.data = data 389 | self.salt = salt 390 | self.person = person 391 | self.fanout = fanout 392 | self.depth = depth 393 | self.leaf_size = leaf_size 394 | self.node_offset = node_offset 395 | self.node_depth = node_depth 396 | self.inner_size = inner_size 397 | self.last_node = last_node 398 | 399 | # now call init routine common to BLAKE2b and BLAKE2s 400 | self._init(key=key) 401 | 402 | 403 | #--------------------------------------------------------------- 404 | 405 | class BLAKE2s(BLAKE2): 406 | 407 | WORDBITS = 32 408 | WORDBYTES = 4 409 | MASKBITS = MASK32BITS 410 | WORDFMT = 'L' # used in _compress() and final() 411 | 412 | ROUNDS = 10 413 | BLOCKBYTES = 64 414 | OUTBYTES = 32 415 | KEYBYTES = 32 416 | SALTBYTES = 8 417 | PERSONALBYTES = 8 418 | 419 | IV = [ 420 | 0x6a09e667, 0xbb67ae85, 421 | 0x3c6ef372, 0xa54ff53a, 422 | 0x510e527f, 0x9b05688c, 423 | 0x1f83d9ab, 0x5be0cd19 424 | ] 425 | 426 | ROT1 = 16 427 | ROT2 = 12 428 | ROT3 = 8 429 | ROT4 = 7 430 | 431 | 432 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - 433 | 434 | def __init__(self, data=b'', digest_size=32, key=b'', 435 | salt=b'', person=b'', fanout=1, depth=1, 436 | leaf_size=0, node_offset=0, node_depth=0, 437 | inner_size=0, last_node=False): 438 | 439 | assert 1 <= digest_size <= self.OUTBYTES 440 | assert len(key) <= self.KEYBYTES 441 | assert len(salt) <= self.SALTBYTES 442 | assert len(person) <= self.PERSONALBYTES 443 | assert 0 <= fanout <= MASK8BITS 444 | assert 0 <= depth <= MASK8BITS 445 | assert 0 <= leaf_size <= MASK32BITS 446 | assert 0 <= node_offset <= MASK48BITS 447 | assert 0 <= node_depth <= MASK8BITS 448 | assert 0 <= inner_size <= MASK8BITS 449 | 450 | # there is a circular class relationship having 451 | # to do with defining the values of SALTBYTES and 452 | # PERSONALBYTES. By creating an empty class and 453 | # loading its contents individually, we get access 454 | # to the parent block's scope and have to define the 455 | # field's values only once. ...but this can look 456 | # confusing. Perhaps it is better to define the 457 | # values 16 and 8 twice and annotate the second 458 | # occurance. It's not like the values will be 459 | # changing often. Which is better? BLAKE2b is 460 | # defined twice and BLAKE2s uses the empty class 461 | # approach. 462 | 463 | class ParamFields32(LittleEndianStructure): 464 | pass 465 | 466 | ParamFields32.SALTBYTES = self.SALTBYTES 467 | ParamFields32.PERSONALBYTES = self.PERSONALBYTES 468 | ParamFields32._fields_ = [ 469 | ("digest_size", c_ubyte), 470 | ("key_length", c_ubyte), 471 | ("fanout", c_ubyte), 472 | ("depth", c_ubyte), 473 | ("leaf_size", c_uint32), 474 | ("node_offset_lo", c_uint32), 475 | ("node_offset_hi", c_uint16), 476 | ("node_depth", c_ubyte), 477 | ("inner_size", c_ubyte), 478 | ("salt", c_char * self.SALTBYTES), 479 | ("person", c_char * self.PERSONALBYTES), 480 | ] 481 | 482 | class Params32(Union): 483 | _fields_ = [("F", ParamFields32), 484 | ("W", c_uint32 * 8), 485 | ] 486 | 487 | # this next makes PARAMS union a 'proper' instance variable 488 | self.PARAMS = Params32 489 | 490 | # key is passed as an argument; all other variables are 491 | # defined as instance variables 492 | self.digest_size = digest_size 493 | self.data = data 494 | self.salt = salt 495 | self.person = person 496 | self.fanout = fanout 497 | self.depth = depth 498 | self.leaf_size = leaf_size 499 | self.node_offset = node_offset 500 | self.node_depth = node_depth 501 | self.inner_size = inner_size 502 | self.last_node = last_node 503 | 504 | # now call init routine common to BLAKE2b and BLAKE2s 505 | self._init(key=key) 506 | 507 | 508 | #--------------------------------------------------------------- 509 | #--------------------------------------------------------------- 510 | #--------------------------------------------------------------- 511 | -------------------------------------------------------------------------------- /demo_blake2.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | doc = """ 4 | 5 | demo_blake2.py -- version 1 6 | 7 | 8 | this runs under Python 2.7 and Python 3.3. 9 | 10 | see blake2.py's README for more information. 11 | 12 | Notes: 13 | 1- printed output prefixed with: 14 | ??? is the result of blake2.py 15 | >>> is the result of blake2x-ref.c 16 | they should agree. 17 | 18 | ----- 19 | 20 | License: 21 | 22 | Copyright (c) 2009-2018 Larry Bugbee, Kent, WA, USA 23 | 24 | Permission to use, copy, modify, and/or distribute this software 25 | for any purpose with or without fee is hereby granted, provided 26 | that the above copyright notice and this permission notice appear 27 | in all copies. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 30 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 31 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 32 | THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 33 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 34 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 35 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 36 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 37 | 38 | (the ISC license, a minor tweak of the BSD license) 39 | 40 | Enjoy, 41 | 42 | Larry Bugbee 43 | December 2013 44 | rev Mar 2018 45 | 46 | """ 47 | 48 | import sys, binascii, platform 49 | from blake2 import BLAKE2b, BLAKE2s 50 | 51 | 52 | #----------------------------------------------------------------------- 53 | 54 | def print_compare_results(actual, expect): 55 | print(' ??? %s' % actual) 56 | print(' >>> %s' % expect) 57 | if actual != expect: 58 | print(' *** results do NOT agree ***') 59 | 60 | #----------------------------------------------------------------------- 61 | 62 | def demo_bfile(filename='blake2.py'): 63 | digest_size = 32 64 | 65 | if len(sys.argv) == 2: 66 | filename = sys.argv[1] 67 | 68 | print('') 69 | print('BLAKE2b of %s (%d-byte digest) - bfile' % (filename, digest_size)) 70 | 71 | data = open(filename, 'rb').read() 72 | print(' datalen: %d' % len(data)) 73 | hexdigest = BLAKE2b(data, digest_size).hexdigest() 74 | 75 | print(' %s - %s' % (hexdigest, filename)) 76 | 77 | 78 | #----------------------------------------------------------------------- 79 | 80 | def demo_b(): 81 | data = b'hello' 82 | digest_size = 64 83 | 84 | print('') 85 | print('BLAKE2b of %s (%d-byte digest) - b' % (data, digest_size)) 86 | print(' datalen: %d' % len(data)) 87 | 88 | b2 = BLAKE2b(digest_size=digest_size) 89 | b2.update(data) 90 | digest = b2.final() 91 | 92 | actual = binascii.hexlify(digest).decode() # or b2.hexdigest() 93 | expect = ('e4cfa39a3d37be31c59609e807970799caa68a19bfaa15135f165085e01d41a6' 94 | + '5ba1e1b146aeb6bd0092b49eac214c103ccfa3a365954bbbe52f74a2b3620c94') 95 | print_compare_results(actual, expect) 96 | 97 | 98 | #----------------------------------------------------------------------- 99 | 100 | def demo_bk(): 101 | data = b'hello' 102 | key = b'secret' 103 | digest_size = 64 104 | 105 | print('') 106 | print('BLAKE2b of %s w/key "%s" (%d-byte digest) - bk' % (data, key, digest_size)) 107 | print(' datalen: %d' % len(data)) 108 | 109 | b2 = BLAKE2b(digest_size=digest_size, key=key) 110 | b2.update(data) 111 | digest = b2.final() 112 | 113 | actual = binascii.hexlify(digest).decode() # or b2.hexdigest() 114 | expect = ('6edf9aa44dfc7590de00fcfdbe2f0d917cdeeb170301416929cc625d19d24edc' 115 | + '1040ff760c1f9bb61ad439a0af5d492fbb01b46ed3feb4e6076383b7885a9486') 116 | print_compare_results(actual, expect) 117 | 118 | #----------------------------------------------------------------------- 119 | 120 | def demo_bksp(): 121 | data = b'hello' 122 | key = b'secret' 123 | salt = b"SALTy" 124 | person = b"this is personal" 125 | digest_size = 64 126 | 127 | print('') 128 | print('BLAKE2b of %s w/key "%s" (%d-byte digest) - bksp' % (data, key, digest_size)) 129 | print(' datalen: %d' % len(data)) 130 | 131 | b2 = BLAKE2b(digest_size=digest_size, key=key, 132 | salt=salt, person=person) 133 | b2.update(data) 134 | digest = b2.final() 135 | 136 | actual = binascii.hexlify(digest).decode() # or b2.hexdigest() 137 | expect = ('99e200174f8abe9ee0d4103fc5be406907d4c5a49fa670ad4cc4a932044bf435' 138 | + '04bce8f0bc3e8b3f7ece823ad433fe76e21208f7dea2deeaa0d32d0d14947035') 139 | print_compare_results(actual, expect) 140 | 141 | #----------------------------------------------------------------------- 142 | 143 | def demo_s(): 144 | data = b'hello' 145 | digest_size = 32 146 | 147 | print('') 148 | print('BLAKE2s of %s (%d-byte digest) - s' % (data, digest_size)) 149 | print(' datalen: %d' % len(data)) 150 | 151 | b2 = BLAKE2s(digest_size=digest_size) 152 | b2.update(data) 153 | digest = b2.final() 154 | 155 | print(' ??? %s' % binascii.hexlify(digest).decode()) 156 | print(' >>> ' 157 | + '19213bacc58dee6dbde3ceb9a47cbb330b3d86f8cca8997eb00be456f140ca25') 158 | 159 | #----------------------------------------------------------------------- 160 | 161 | def demo_sk(): 162 | data = b'hello' 163 | key = b'secret' 164 | digest_size = 32 165 | 166 | print('') 167 | print('BLAKE2s of %s w/key "%s" (%d-byte digest) - sk' % (data, key, digest_size)) 168 | print(' datalen: %d' % len(data)) 169 | 170 | b2 = BLAKE2s(digest_size=digest_size, key=key) 171 | b2.update(data) 172 | digest = b2.final() 173 | 174 | print(' ??? %s' % binascii.hexlify(digest).decode()) 175 | print(' >>> ' 176 | + 'a9d4269517517303b7d086e51876ee881d315fab3fe9044e5e4c7e7cad7253be') 177 | 178 | #----------------------------------------------------------------------- 179 | 180 | def demo_sksp(): 181 | data = b'hello' 182 | key = b'secret' 183 | salt = b"SALTy" 184 | person = b"personal" 185 | digest_size = 32 186 | 187 | print('') 188 | print('BLAKE2s of %s w/key "%s" (%d-byte digest) - sksp' % (data, key, digest_size)) 189 | print(' datalen: %d' % len(data)) 190 | 191 | b2 = BLAKE2s(digest_size=digest_size, key=key, 192 | salt=salt, person=person) 193 | b2.update(data) 194 | digest = b2.final() 195 | 196 | print(' salt = "%s", person = "%s"' % (salt, person)) 197 | print(' ??? %s' % binascii.hexlify(digest).decode()) 198 | print(' >>> ' 199 | + '55ece82d0647dcefd7d1af675c62fb25a2b2388e5e277d9728d0f1ab45ca9fb3') 200 | 201 | #----------------------------------------------------------------------- 202 | 203 | def demo_b2(): 204 | data = b""" 205 | 206 | 207 | The Gettysburg Address 208 | Gettysburg, Pennsylvania 209 | November 19, 1863 210 | 211 | 212 | Four score and seven years ago our fathers brought forth on this 213 | continent, a new nation, conceived in Liberty, and dedicated to the 214 | proposition that all men are created equal. 215 | 216 | Now we are engaged in a great civil war, testing whether that nation, 217 | or any nation so conceived and so dedicated, can long endure. We are 218 | met on a great battle-field of that war. We have come to dedicate a 219 | portion of that field, as a final resting place for those who here 220 | gave their lives that that nation might live. It is altogether fitting 221 | and proper that we should do this. 222 | 223 | But, in a larger sense, we can not dedicate -- we can not consecrate 224 | -- we can not hallow -- this ground. The brave men, living and dead, 225 | who struggled here, have consecrated it, far above our poor power to 226 | add or detract. The world will little note, nor long remember what we 227 | say here, but it can never forget what they did here. It is for us the 228 | living, rather, to be dedicated here to the unfinished work which they 229 | who fought here have thus far so nobly advanced. It is rather for us 230 | to be here dedicated to the great task remaining before us -- that 231 | from these honored dead we take increased devotion to that cause for 232 | which they gave the last full measure of devotion -- that we here 233 | highly resolve that these dead shall not have died in vain -- that 234 | this nation, under God, shall have a new birth of freedom -- and that 235 | government of the people, by the people, for the people, shall not 236 | perish from the earth. 237 | 238 | 239 | -- Abraham Lincoln 240 | 241 | """ 242 | digest_size = 64 243 | 244 | print('') 245 | print('BLAKE2b of %s (%d-byte digest) - b2' % ('Gettysburg Address', digest_size)) 246 | print(' datalen: %d' % len(data)) 247 | 248 | b2 = BLAKE2b(digest_size=digest_size) 249 | b2.update(data) 250 | digest = b2.final() 251 | 252 | actual = binascii.hexlify(digest).decode() # or b2.hexdigest() 253 | expect = ('d1e31c4b3b68a12bf6df4a35b94feb2409ba8dd0b1a19ca0cb4aebce518ed8d5' 254 | + '2d860c22db39f297483eead5b4e8f2bda955da2b22eb08bcf4e3b047906a757f') 255 | print_compare_results(actual, expect) 256 | 257 | 258 | #----------------------------------------------------------------------- 259 | 260 | def demo_s2(): 261 | data = b'hello world' 262 | 263 | print('') 264 | print('BLAKE2s of %s (%d-byte digest) - s2' % (b'hello world', 32)) 265 | print(' datalen: %d' % len(data)) 266 | 267 | digest = BLAKE2s(b'hello world').final() 268 | 269 | print(' ??? %s' % binascii.hexlify(digest).decode()) 270 | print(' >>> ' 271 | + '9aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b') 272 | 273 | #----------------------------------------------------------------------- 274 | 275 | def demo_errs(): 276 | data = b'hello' 277 | key = b'secret' 278 | salt = b"SALTy" 279 | person = b"personal" 280 | digest_size = 32 281 | 282 | print('') 283 | print('BLAKE2s of %s w/key "%s" (%d-byte digest) - errs' % (data, key, digest_size)) 284 | print(' datalen: %d' % len(data)) 285 | 286 | b2 = blake2.BLAKE2(digest_size=digest_size, key=key, 287 | salt=salt, person=person) 288 | b2.update(data) 289 | digest = b2.final() 290 | 291 | print(' salt = "%s", person = "%s"' % (salt, person)) 292 | print(' ??? %s' % binascii.hexlify(digest).decode()) 293 | print(' >>> ' 294 | + '55ece82d0647dcefd7d1af675c62fb25a2b2388e5e277d9728d0f1ab45ca9fb3') 295 | 296 | #----------------------------------------------------------------------- 297 | 298 | def tree(): 299 | title = "Dimetry's tree hash example" 300 | print('') 301 | print(title) 302 | 303 | FANOUT = 2 304 | DEPTH = 2 305 | LEAF_SIZE = 4096 306 | INNER_SIZE = 64 307 | 308 | buf = bytearray(6000) 309 | 310 | # Left leaf 311 | h00 = BLAKE2b(buf[0:LEAF_SIZE], fanout=FANOUT, depth=DEPTH, 312 | leaf_size=LEAF_SIZE, inner_size=INNER_SIZE, 313 | node_offset=0, node_depth=0, last_node=False) 314 | h00_digest = h00.final() 315 | 316 | # Right leaf 317 | h01 = BLAKE2b(buf[LEAF_SIZE:], fanout=FANOUT, depth=DEPTH, 318 | leaf_size=LEAF_SIZE, inner_size=INNER_SIZE, 319 | node_offset=1, node_depth=0, last_node=True) 320 | h01_digest = h01.final() 321 | 322 | # Root node 323 | h10 = BLAKE2b(digest_size=32, fanout=FANOUT, depth=DEPTH, 324 | leaf_size=LEAF_SIZE, inner_size=INNER_SIZE, 325 | node_offset=0, node_depth=1, last_node=True) 326 | h10.update(h00_digest) 327 | h10.update(h01_digest) 328 | 329 | print(' ??? %s' % h10.hexdigest()) 330 | print(' >>> ' 331 | + '3ad2a9b37c6070e374c7a8c508fe20ca86b6ed54e286e93a0318e95e881db5aa') 332 | 333 | 334 | #----------------------------------------------------------------------- 335 | #----------------------------------------------------------------------- 336 | 337 | if __name__ == '__main__': 338 | 339 | print('') 340 | print(platform.python_version()) 341 | 342 | if 1: 343 | # < 1blk 344 | demo_b() 345 | demo_bk() 346 | demo_bksp() 347 | demo_s() 348 | demo_sk() 349 | demo_sksp() 350 | 351 | if 1: 352 | # > 1blk 353 | demo_b2() 354 | # demo_bfile() 355 | 356 | if 9: 357 | 358 | tree() 359 | 360 | if 0: 361 | # special 362 | demo_s2() 363 | demo_errs() 364 | pass 365 | 366 | print('') 367 | 368 | #----------------------------------------------------------------------- 369 | #----------------------------------------------------------------------- 370 | #----------------------------------------------------------------------- 371 | --------------------------------------------------------------------------------