Post
Topic
Board Bitcoin Technical Support
Re: A plea for help :)
by
Paulfontrahel
on 08/07/2025, 18:01:31 UTC
I had electrum, installed it on Windows 7, I can't say exactly what version of the wallet it was, I don't remember. As for bias, I asked gpt chat to analyze all my signatures from the blockchain
AFAIK Electrum software never had any issues with its RNG to generate any kind of bias when it created signatures even in the older versions (I've used it since 1.9.Cool. And considering you say an AI found bias, it is another reason to be skeptical about existence of an actual bias in the signatures...



I don’t know, I’m not claiming anything, I’m just saying I really want to believe that what he found is true. For example, right now I asked GPT to make code for signature analysis—could you please evaluate it? Is the code decent?


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ecdsa_leakage_analysis.py

Automated ECDSA signature leakage analysis via predefined channels:
  - Information metric I = 1 − H(p) for binary predicates
  - χ²-test for 8-bit window uniformity
  - PCA across all channels (components up to 95% variance)
  - Channel autocorrelation (lags 1–20)
  - Single-bit scan (256 positions)
"""

import argparse
import math
from collections import Counter

import numpy as np
import pandas as pd
from scipy.stats import chi2
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

def load_signatures(path):
    R, S, Z = [], [], []
    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line.startswith('R ='):
                R.append(int(line.split('=',1)[1], 16))
            elif line.startswith('S ='):
                S.append(int(line.split('=',1)[1], 16))
            elif line.startswith('Z ='):
                Z.append(int(line.split('=',1)[1], 16))
    assert len(R)==len(S)==len(Z), "R/S/Z mismatch"
    return R, S, Z

def build_raw_channels(R, S, Z):
    m = len(R)
    raw = {}
    # Predefined windows
    WINDOW_SHIFTS = [
        (65,  'R'), (71,  'R'),
        (140, 'S'), (141, 'S'), (143, 'S'),
        (145, 'S'), (146, 'S'),
        (100, 'Z'), (132, 'Z'),
    ]
    for shift, typ in WINDOW_SHIFTS:
        seq = {'R':R,'S':S,'Z':Z}[typ]
        vals = [(v>>shift)&0xFF for v in seq]
        mode = Counter(vals).most_common(1)[0][0]
        raw[f"{typ}_win{shift}_8"] = [1 if v==mode else 0 for v in vals]
    # Bit-level
    raw['R_bit12']   = [(r>>12)&1 for r in R]
    raw['R_bit168']  = [(r>>168)&1 for r in R]
    raw['R_MSB']     = [(r>>255)&1 for r in R]
    # Conjunction
    s140 = raw['S_win140_8']
    raw['R_and168_140'] = [raw['R_bit168'] & s140 for i in range(len(R))]
    # Modular
    for mod in (41,67,79):
        vals = [s%mod for s in S]
        mode = Counter(vals).most_common(1)[0][0]
        raw[f"S_mod{mod}"] = [1 if v==mode else 0 for v in vals]
    return raw

def binary_info(vec):
    p = sum(vec)/len(vec)
    if p in (0,1):
        return 0.0
    H = -p*math.log2(p) - (1-p)*math.log2(1-p)
    return max(0.0, 1-H)

def chi2_windows(seq):
    """χ²-test for all 8-bit windows; returns DataFrame."""
    m = len(seq)
    res = []
    for shift in range(0,256-8+1):
        vals = [(v>>shift)&0xFF for v in seq]
        counts = np.bincount(vals, minlength=256)
        expected = m/256
        chi2_stat = ((counts - expected)**2 / expected).sum()
        pval = 1 - chi2.cdf(chi2_stat, df=255)
        res.append((shift, chi2_stat, pval))
    return pd.DataFrame(res, columns=['Shift','Chi2','p-value'])

def pca_analysis(channel_matrix, n_comp=10):
    pca = PCA(n_components=n_comp)
    pca.fit(channel_matrix)
    ev = pca.explained_variance_ratio_
    cev = np.cumsum(ev)
    return pd.DataFrame({
        'PC': np.arange(1,n_comp+1),
        'Explained': ev,
        'Cumulative': cev
    })

def autocorr_channels(raw, max_lag=20):
    df = {}
    for ch, vec in raw.items():
        s = pd.Series(vec)
        df[ch] = [s.autocorr(lag) for lag in range(1, max_lag+1)]
    return pd.DataFrame(df, index=range(1,max_lag+1))

def bit_scan(seq):
    m = len(seq)
    res = []
    for bit in range(256):
        v = [(x>>bit)&1 for x in seq]
        I = binary_info(v)
        res.append((bit,I))
    return pd.DataFrame(res, columns=['Bit','Info'])

def main(args):
    R, S, Z = load_signatures(args.input)
    raw = build_raw_channels(R,S,Z)

    # 1) Channel information
    info = [(ch, binary_info(vec)) for ch, vec in raw.items()]
    info_df = pd.DataFrame(info, columns=['Channel','Info']).sort_values('Info', ascending=False)

    # 2) χ²
    chi2_r = chi2_windows(R).sort_values('Chi2', ascending=False).head(5)
    chi2_s = chi2_windows(S).sort_values('Chi2', ascending=False).head(5)
    chi2_z = chi2_windows(Z).sort_values('Chi2', ascending=False).head(5)

    # 3) PCA
    chan_mat = np.array(list(raw.values())).T
    pca_df = pca_analysis(chan_mat, n_comp=min(16, chan_mat.shape[1]))

    # 4) Autocorrelation
    auto_df = autocorr_channels(raw, max_lag=20)

    # 5) Bit-scan
    bits_r = bit_scan(R).sort_values('Info', ascending=False).head(5)
    bits_s = bit_scan(S).sort_values('Info', ascending=False).head(5)
    bits_z = bit_scan(Z).sort_values('Info', ascending=False).head(5)

    # Output
    print("\n=== Top Channels by Information ===")
    print(info_df.head(10).to_string(index=False))

    print("\n=== Top χ² Windows R ===")
    print(chi2_r.to_string(index=False))
    print("\n=== Top χ² Windows S ===")
    print(chi2_s.to_string(index=False))
    print("\n=== Top χ² Windows Z ===")
    print(chi2_z.to_string(index=False))

    print("\n=== PCA Explained Variance ===")
    print(pca_df.to_string(index=False))

    print("\n=== Top Bit Leakages R ===")
    print(bits_r.to_string(index=False))
    print("\n=== Top Bit Leakages S ===")
    print(bits_s.to_string(index=False))
    print("\n=== Top Bit Leakages Z ===")
    print(bits_z.to_string(index=False))

    # Plots
    plt.figure(figsize=(6,4))
    plt.plot(pca_df['PC'], pca_df['Cumulative'], marker='o')
    plt.axhline(0.95, color='red', linestyle='--', label='95%')
    plt.xlabel("PC")
    plt.ylabel("Cumulative explained var.")
    plt.title("PCA on Channels")
    plt.legend()
    plt.tight_layout()
    plt.show()

    auto_df.plot(figsize=(8,4), legend=False)
    plt.title("Autocorrelation (lags 1–20)")
    plt.xlabel("Lag")
    plt.ylabel("Autocorr")
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--input", required=True,
                   help="Signature file (rsz.txt)")
    args = p.parse_args()
    main(args)



If not, you'll have to take a deep breath, go get a drink and calm down Smiley It would seem funny, if it weren't so sad Smiley