I'm trying to wrap my head around going from a seed to a SigningKey, and also obtaining a PrivateKey (encryption key). I'm using NaCl / libsodium.
I created the code below and the results are interesting. Turns out pk1.private_key and pk2.private_key match roughly 3% of the time. However public key matches 100%, all are generated starting with the same seed. What is going on here?
Examples of mismatch (they're close but aren't equal):
# 1st byte mismatch by 0x01
seed:  f8d9e54a23971beebf2552c1a50ade6150cd051321398394f515e8d4b1ba0404
priv1: c1fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048
priv2: c0fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048
# 1st byte mismatch by 0x03
seed:  d612a66f92ee2f42ab1f7ea9a712a47c815843d21fc988b1d202459f235b6410
priv1: f33d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe86bb
priv2: f03d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe867b
# 1st byte mismatch by 0x01, last byte mismatch by 0x40
seed:  10b7e1c66cf08005a22289158a088e028160f892dc6c20d43025be4690aaed85
priv1: 194898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02109
priv2: 184898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02149
from nacl.signing import SigningKey
from nacl.public import PrivateKey, PublicKey, Box, SealedBox
from nacl.bindings import crypto_sign_SEEDBYTES
from nacl.utils import StringFixer, random
def run(debug=False):
    seed = random(crypto_sign_SEEDBYTES)
    pk1 = PrivateKey.from_seed(seed)
    pk2 = SigningKey(seed).to_curve25519_private_key()
    if debug:
        print('seed:  ', seed.hex()) 
        print('priv1: ', pk1._private_key.hex())
        print('priv2: ', pk2._private_key.hex())
        print('pub1:  ', bytes(pk1.public_key).hex())
        print('pub2:  ', bytes(pk2.public_key).hex())
    return seed, pk1, pk2
runs = 10000
private_key_match = 0
public_key_match = 0
both_match = 0
for i in range(runs):
    if i % 500 == 0:
        print(i, 'of', runs)
    seed, pk1, pk2 = run()
    x = pk1._private_key == pk2._private_key
    y = bytes(pk1.public_key) == bytes(pk2.public_key)
    if x:
        private_key_match += 1
    if y:
        public_key_match += 1
    if x and y:
        both_match += 1
print('private key match:', private_key_match)
print('public key match: ', public_key_match)
print('both match:       ', both_match)