Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

crates/gunnar-crypt/tests/vectors/check-security-key.py

#!/usr/bin/env python3
"""An independent implementation of gunnar-crypt's security-key derivation
(src/userkey.rs `security_key`), for the vectors in tests/userkey.rs.
Python `cryptography` (HKDF-SHA256, X25519) and BIP-173's reference bech32;
no gunnar code. TEST inputs only."""
import hashlib
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey

CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def polymod(values):
    gen = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
    chk = 1
    for v in values:
        b = chk >> 25
        chk = (chk & 0x1ffffff) << 5 ^ v
        for i in range(5):
            chk ^= gen[i] if ((b >> i) & 1) else 0
    return chk
def hrp_expand(hrp): return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
def convertbits(data, frombits, tobits):
    acc = bits = 0; ret = []; maxv = (1 << tobits) - 1
    for v in data:
        acc = (acc << frombits) | v; bits += frombits
        while bits >= tobits:
            bits -= tobits; ret.append((acc >> bits) & maxv)
    if bits: ret.append((acc << (tobits - bits)) & maxv)
    return ret
def bech32(hrp, data):
    d = convertbits(data, 8, 5)
    pm = polymod(hrp_expand(hrp) + d + [0] * 6) ^ 1
    return hrp + "1" + "".join(CHARSET[x] for x in d + [(pm >> 5 * (5 - i)) & 31 for i in range(6)])

LABEL = b"gunnar-crypt security-key x25519 v1"
PRF_INPUT = b"gunnar-crypt user-key unlock v1"
print("hmac-secret salt", hashlib.sha256(b"WebAuthn PRF\x00" + PRF_INPUT).hexdigest())
for prf, cred in [(bytes(range(32)), bytes(range(1, 17))), (bytes([0xA5] * 32), bytes(range(64)))]:
    secret = HKDF(algorithm=hashes.SHA256(), length=32, salt=LABEL, info=LABEL + b"\x00" + cred).derive(prf)
    pub = X25519PrivateKey.from_private_bytes(secret).public_key().public_bytes(
        serialization.Encoding.Raw, serialization.PublicFormat.Raw)
    print(prf.hex(), cred.hex(), bech32("age", pub))