Source code for ecpy.ecrand

# Copyright 2016 Cedric Mesnil <cedric.mesnil@ubinity.com>, Ubinity SAS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#python 2 compatibility
from builtins import int

from ecpy.curves     import Curve,Point
from ecpy.keys       import ECPublicKey, ECPrivateKey
from ecpy.formatters import decode_sig, encode_sig

import random
import hmac


[docs]def rnd(q): """ Returns a random number less than q, with the same bits length than q Args: q (int) : field/modulo Returns: int : random """ nbits = q.bit_length() while True: k = random.getrandbits(nbits) if k<q: return k
[docs]def rnd_rfc6979(hashmsg, secret, q, hasher, V = None): """ Generates a deterministic `value` according to RF6979. See https://tools.ietf.org/html/rfc6979#section-3.2 if V == None, this is the first try, so compute the initial value for V. Else it means the previous value has been rejected by the caller, so generate the next one! Warning: the `hashmsg` parameter is the message hash, not the message itself. In other words, `hashmsg` is equal to `h1` in the *rfc6979, section-3.2, step a*. Args: hasher (hashlib): hasher hashmsg (bytes) : message hash secret (int) : secret q (int) : field/modulo V : previous value for continuation The function returns a couple `(k,V)` with `k` the expected value and `V` is the continuation value to pass to next cal if k is rejected. Returns: tuple: (k,V) """ if (V == None): #a. Process m through the hash function H, yielding: h1 = H(m) #h1 = hasher(msg).digest() h1 = hashmsg hsize = len(h1) #b. Set: V = 0x01 0x01 0x01 ... 0x01 V = b'\x01'*hsize #c. Set: K = 0x00 0x00 0x00 ... 0x00 K = b'\x00'*hsize #d. Set: K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) x = secret.to_bytes(hsize,'big') K = hmac.new(K,V + b'\x00' + x + h1,hasher).digest() #e. Set: V = HMAC_K(V) V = hmac.new(K,V,hasher).digest() #f. Set: K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1)) K = hmac.new(K,V + b'\x01' + x + h1,hasher).digest() #g. Set: V = HMAC_K(V) V = hmac.new(K,V,hasher).digest() #h. Apply the following algorithm until a proper value is found for k: while True: # # 1. Set T to the empty sequence. The length of T (in bits) is # denoted tlen; thus, at that point, tlen = 0. T = b'' # 2. While tlen < qlen, do the following: # V = HMAC_K(V) # T = T || V q_blen = q.bit_length() while len(T)*8<q_blen : V = hmac.new(K, V, hasher).digest() T = T + V #3. Compute: k = int.from_bytes(T,'big') k_blen = k.bit_length() if k_blen > q_blen : k = k >> (k_blen - q_blen) # If that value of k is within the [1,q-1] range, and is # suitable for DSA or ECDSA (i.e., it results in an r value # that is not 0; see Section 3.4), then the generation of k is # finished. The obtained value of k is used in DSA or ECDSA. if k > 0 and k < (q-1): return k,V # Otherwise, compute: # K = HMAC_K(V || 0x00) # V = HMAC_K(V) # and loop (try to generate a new T, and so on). K = hmac.new(K, V+b'\x00', hasher).digest() V = hmac.new(K, V, hasher).digest()
if __name__ == "__main__": import hashlib h = 0xaf9ae10ca04f826d5ff4727f97fb568c79e9ffa9686b9d5deb4ea4db44d6f23d h = h.to_bytes(32,'big') secret = 0xe7244dd97b3558788fbf02f443d9a6ebd12a1ab01703a683aa12412354a43218 q = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f hashlib.sha256 r,V = rnd_rfc6979(h, secret, q, hashlib.sha256) assert(r == 0xbf13da837bcfa314f30fa68be3a9219ca244c1b36f0440624ba88406fcd7462d)