Building mochimo_wots: A Dart Implementation of Mochimo WOTS Cryptography

Building mochimo_wots: A Dart Implementation of Mochimo WOTS Cryptography

August 9, 2026 14 views
SHARE Twitter / X LinkedIn Facebook

mochimo_wots is my Dart/Flutter library for the Mochimo network’s Winternitz One-Time Signature (WOTS) protocol — hash chains, 2208-byte addresses, signing/verification, tagged identities, datagram framing, and CRC checks — published as a reusable package.

I built it as Prakash Niraula, System Engineer at AI Kensetsu Co., Ltd in Sakai, Osaka. The goal was not a demo UI — it was a correct, testable protocol implementation other Flutter developers can depend on.


Why WOTS is hard to implement well

WOTS is a hash-based one-time signature scheme. Security comes from one-way hash chains, not elliptic curves. That changes the engineering:

  • One-time use — a WOTS keypair should sign once; reuse leaks secret material.
  • Large keys — Mochimo uses 2144 bytes of signature/public-key material, not a 64-byte curve sig.
  • Exact layouts — addresses are 2208 bytes; datagrams are 8920 bytes. Endian mistakes break peers silently.
  • Protocol fidelity — chain lengths, checksum digits, tag placement, and CRC must match the network.

This is systems + cryptography work: buffers, little-endian packing, PRFs, and interoperability tests.

What the package includes

Current release: v1.0.3 (Dart SDK ≥ 3.0, Flutter ≥ 3.0).

  • WOTS core — seed expansion, base-w encoding, checksum, chains, sign, verify
  • Wallet API — deterministic create, sign/verify, secure clear()
  • Tags — 12-byte tags + Base58 helpers
  • Datagram codec — serialize/parse network frames + capabilities
  • UtilitiesMochimoHasher, ByteBuffer, ByteUtils, CRC16
  • Tests & examples — unit suites for each major module

Protocol constants

wots parameters

WOTSW         = 16
WOTSLOGW      = 4
PARAMSN       = 32
WOTSLEN1      = 64
WOTSLEN2      = 3
WOTSLEN       = 67
WOTSSIGBYTES  = 2144   // 67 × 32
Address       = 2208   // 2144 + 32 + 32
Tag           = 12
Datagram      = 8920

If these drift, local round-trips can still “pass” while network peers reject you.

Package layout

lib/

lib/
  mochimo_wots.dart
  core/
    hasher/mochimo_hasher.dart
    model/byte_buffer.dart
    protocol/
      wots.dart
      wots_hash.dart
      wots_address.dart
      wots_wallet.dart
      tags.dart
      datagram.dart
    utils/
      byte_utils.dart
      crc16.dart
      tag_utils.dart

Keep protocol math pure and testable; expose WOTSWallet as the safer app-facing API.

Install

pubspec.yaml

dependencies:
  mochimo_wots: ^1.0.3

terminal

dart pub get
# or
flutter pub get

Address layout (2208 bytes)

  1. 0–2143 — WOTS public key chains (67 × 32)
  2. 2144–2175 — public seed (32)
  3. 2176–2207 — address/rnd seed (32), with optional 12-byte tag region

Tags give a shorter identity; full WOTS material is still required for verification.

Create a wallet (correct path)

Use WOTSWallet.create(...). It expands the secret, builds the 2208-byte address, and attaches a tag.

dart — create wallet

import 'dart:typed_data';
import 'package:mochimo_wots/mochimo_wots.dart';
import 'package:mochimo_wots/core/utils/byte_utils.dart';

void main() {
  // Production: CSPRNG / secure keystore — never hardcode secrets.
  final secret = Uint8List(32);
  for (var i = 0; i < 32; i++) {
    secret[i] = i + 1;
  }

  final tag = Uint8List(12)..fillRange(0, 12, 0x34);

  final wallet = WOTSWallet.create('Main Wallet', secret, tag);

  print(wallet.getAddrTagHex());
  print(wallet.getAddrTagBase58());

  final wots = wallet.getWots();
  if (wots != null) {
    print('len=${wots.length}'); // 2208
    print(ByteUtils.bytesToHex(wots.sublist(0, 32)));
  }

  wallet.clear();
}

Tip: Default component derivation hashes seed || "seed", seed || "publ", and seed || "addr". Keep those labels stable forever or addresses will diverge.

Sign and verify

Signing walks each chain to a message-dependent height. Verification finishes each chain to the public tip and compares the reconstructed public key.

dart — sign / verify

import 'dart:convert';
import 'dart:typed_data';
import 'package:mochimo_wots/mochimo_wots.dart';

void main() {
  final secret = Uint8List(32)..fillRange(0, 32, 0x56);
  final tag = Uint8List(12)..fillRange(0, 12, 0x34);

  final wallet = WOTSWallet.create('Signer', secret, tag);
  final message = Uint8List.fromList(utf8.encode('Hello, Mochimo!'));

  final signature = wallet.sign(message); // 2144 bytes
  print(wallet.verify(message, signature)); // true

  final bad = Uint8List.fromList(message)..[0] ^= 0x01;
  print(wallet.verify(bad, signature)); // false

  wallet.clear();
}

Security: The API can sign more than once, but production spend paths must treat WOTS keys as one-time and rotate after use.

Low-level WOTS control

dart — generateAddress

import 'dart:typed_data';
import 'package:mochimo_wots/core/protocol/wots.dart';
import 'package:mochimo_wots/core/protocol/wots_wallet.dart';
import 'package:mochimo_wots/core/utils/byte_utils.dart';

void main() {
  final secret = Uint8List(32)..fillRange(0, 32, 0x12);
  final tag = Uint8List(12)..fillRange(0, 12, 0xAB);

  final address = WOTS.generateAddress(
    tag,
    secret,
    WOTSWallet.componentsGenerator,
  );

  print(ByteUtils.bytesToHex(address.sublist(0, 64)));
  print(WOTS.isValid(secret, address));
}

Core primitives: expandSeed, baseW_, wotsChecksum, genChain, wotsSign, wotsPkFromSig.

ByteBuffer and endianness

Most chain bugs are binary I/O bugs. The package includes an explicit-endian ByteBuffer because datagram fields are little-endian in many places, while some crypto counters are big-endian.

dart — byte buffer

import 'package:mochimo_wots/mochimo_wots.dart';
import 'package:mochimo_wots/core/utils/byte_utils.dart';

void main() {
  final buffer = ByteBuffer.allocate(16);
  buffer.order(ByteOrder.BIG_ENDIAN).putInt(0x12345678);
  buffer.order(ByteOrder.LITTLE_ENDIAN).putInt(0x9ABCDEF0);
  buffer.rewind();
  print(ByteUtils.bytesToHex(buffer.array()));
}

Datagram framing

Datagram encodes an 8920-byte frame: version/flags, network + session ids, operation codes, block metadata, three 2208-byte addresses, amounts, 2144-byte signature, CRC16, trailer.

dart — datagram

import 'package:mochimo_wots/mochimo_wots.dart';

void main() {
  final dg = Datagram()
    ..setId1(1)
    ..setId2(2)
    ..setOperation(Operation.balanceRequest)
    ..setCurrentBlockHeight(BigInt.from(1000))
    ..setAddToPeerList(true);

  final bytes = dg.serialize();
  print(bytes.length); // 8920

  final parsed = Datagram.of(bytes);
  print(parsed.getOperation());
  print(parsed.getCRC());
}

Hashing helpers

dart — MochimoHasher

import 'dart:typed_data';
import 'package:mochimo_wots/core/hasher/mochimo_hasher.dart';
import 'package:mochimo_wots/core/utils/byte_utils.dart';

void main() {
  final data = Uint8List.fromList('mochimo'.codeUnits);
  print(ByteUtils.bytesToHex(MochimoHasher.hash(data)));
  print(ByteUtils.bytesToHex(MochimoHasher.hashWith('sha3-512', data)));
  print(ByteUtils.bytesToHex(MochimoHasher.hashWith('ripemd160', data)));
}

Dependencies stay small: PointyCastle, base58check, meta, Flutter SDK.

Skills this project required

  1. Cryptographic literacy — WOTS chains, checksum digits, one-time key discipline
  2. Binary protocol design — fixed widths, endianness, CRC framing
  3. Dart systems workUint8List, copies vs views, secret wiping
  4. API design — low-level WOTS + higher-level WOTSWallet
  5. Test discipline — hasher, tags, wallet, address, datagram, WOTS suites
  6. Open-source packaging — pub.dev metadata, examples, MIT, versioned API

Pitfalls worth documenting

  1. Prefer WOTSWallet.create — a bare constructor may not synthesize a full 2208-byte address.
  2. Tags are 12 bytes — older 20-byte assumptions will throw.
  3. Component labels matter — default is seed / publ / addr; custom KDFs must stay consistent.
  4. Never ship demo secrets — fill bytes like 0x12 / 0x56 are tests only.
  5. Validate on the wire — local verify can pass while datagram CRC/endian still fails peers.

Quick checklist

  1. Add mochimo_wots: ^1.0.3
  2. Generate a secure 32-byte secret
  3. WOTSWallet.create('name', secret, tag12)
  4. Persist public address/tag; protect the secret
  5. Sign → verify → then broadcast
  6. Use Datagram if you speak the peer protocol
  7. Call wallet.clear() when disposing sensitive state

Most of my public work is full-stack and operational — SaaS like Web Converter Tools, CRM systems, Flutter apps, Laravel/Next.js, and infrastructure. mochimo_wots is the cryptographic counterpart: protocol-level Dart, published and documented.

Contact Prakash Niraula →