public bool TryGetNonce(Span <byte> nonce32, ReadOnlySpan <byte> msg32, ReadOnlySpan <byte> key32, ReadOnlySpan <byte> algo16, uint counter) { Span <byte> keydata = stackalloc byte[112]; Span <byte> originalKeyData = keydata; int offset = 0; using RFC6979HMACSHA256 rng = new RFC6979HMACSHA256(); uint i; /* We feed a byte array to the PRNG as input, consisting of: * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. * - optionally 16 extra bytes with the algorithm name. * Because the arguments have distinct fixed lengths it is not possible for * different argument mixtures to emulate each other and result in the same * nonces. */ key32.CopyTo(keydata); keydata = keydata.Slice(32); offset += 32; msg32.CopyTo(keydata); keydata = keydata.Slice(32); offset += 32; if (this.data != null) { this.data.CopyTo(keydata); keydata = keydata.Slice(32); offset += 32; } if (algo16.Length == 16) { algo16.CopyTo(keydata); keydata = keydata.Slice(16); offset += 16; } rng.Initialize(originalKeyData.Slice(0, offset)); originalKeyData.Fill(0); for (i = 0; i <= counter; i++) { rng.Generate(nonce32); } return(true); }
public void Blind(byte[]?seed32 = null) { Scalar b; GEJ gb; FE s; Span <byte> nonce32 = stackalloc byte[32]; using RFC6979HMACSHA256 rng = new RFC6979HMACSHA256(); bool retry; Span <byte> keydata = stackalloc byte[64]; if (seed32 == null) { /* When seed is NULL, reset the initial point and blinding value. */ this.initial = EC.G.ToGroupElementJacobian(); this.initial = this.initial.Negate(); this.blind = new Scalar(1); } /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ this.blind.WriteToSpan(nonce32); /* Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, * and guards against weak or adversarial seeds. This is a simpler and safer interface than * asking the caller for blinding values directly and expecting them to retry on failure. */ nonce32.CopyTo(keydata); if (seed32 != null) { seed32.AsSpan().CopyTo(keydata.Slice(32, 32)); } else { keydata = keydata.Slice(0, 32); } rng.Initialize(keydata); keydata.Clear(); /* Retry for out of range results to achieve uniformity. */ do { rng.Generate(nonce32); retry = !FE.TryCreate(nonce32, out s); retry |= s.IsZero; } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ /* Randomize the projection to defend against multiplier sidechannels. */ this.initial = this.initial.Rescale(s); FE.Clear(ref s); do { rng.Generate(nonce32); b = new Scalar(nonce32, out int overflow); retry = overflow == 1; /* A blinding value of 0 works, but would undermine the projection hardening. */ retry |= b.IsZero; } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ rng.Dispose(); nonce32.Fill(0); gb = MultGen(b); b = b.Negate(); this.blind = b; this.initial = gb; Scalar.Clear(ref b); GEJ.Clear(ref gb); }