feat(mobile): reconstruir+ampliar binding gomobile para paridad con la web
El wrapper mobile/unibus.go se habia perdido del repo (solo quedaba compilado
en el .aar del 5 jun). Se reconstruye y amplia con:
- Wallet BIP39 determinista: NewMnemonic, ValidateMnemonic, DeriveAndSaveIdentity.
Deriva la MISMA identidad que uniweb (web/src/wallet/derive.ts): PBKDF2-BIP39 ->
HKDF-SHA256(info unibus-sign-v1 / unibus-kex-v1) -> Ed25519 + X25519. Test de
paridad contra el vector de oro (mnemonica abandon...about -> sign_pub
34302746...b3c8) garantiza misma cuenta web<->movil byte a byte.
- Selector de salas: Session.ListMyRooms() -> JSON [{id,subject,mode,role}].
- Nombres legibles: Session.Directory() + Client.Directory()/EndpointID() nuevos
en pkg/client (GET /directory firmado).
- HasIdentity/SignPubAt para el onboarding.
Aditivo; build/vet/test del modulo verdes (incluido TestDeriveParityWithWeb).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package mobile
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestDeriveParityWithWeb pins the Go wallet derivation to the TypeScript one
|
||||
// (web/src/wallet/derive.ts). The canonical BIP39 test mnemonic must derive to this
|
||||
// exact Ed25519 sign_pub — the same value the uniweb client showed for this phrase.
|
||||
// If this fails, web and mobile would derive different identities from the same seed
|
||||
// and the "same account on both devices" guarantee breaks.
|
||||
func TestDeriveParityWithWeb(t *testing.T) {
|
||||
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
const wantSignPub = "34302746268e7370d35940e1bcef8c0b1c13a857ea6209e6ecc6e9b3af06b3c8"
|
||||
|
||||
id, err := deriveIdentity(mnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("deriveIdentity: %v", err)
|
||||
}
|
||||
if got := hex.EncodeToString(id.SignPub); got != wantSignPub {
|
||||
t.Fatalf("sign_pub mismatch:\n got %s\n want %s", got, wantSignPub)
|
||||
}
|
||||
if len(id.SignPriv) != 64 || len(id.KexPub) != 32 || len(id.KexPriv) != 32 {
|
||||
t.Fatalf("bad key lengths: signPriv=%d kexPub=%d kexPriv=%d",
|
||||
len(id.SignPriv), len(id.KexPub), len(id.KexPriv))
|
||||
}
|
||||
// sign_priv is Go's ed25519 layout: seed || pub, so its tail must equal sign_pub.
|
||||
if got := hex.EncodeToString(id.SignPriv[32:]); got != wantSignPub {
|
||||
t.Fatalf("sign_priv tail != sign_pub:\n got %s\n want %s", got, wantSignPub)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMnemonicRoundTrip checks a freshly generated phrase validates and derives.
|
||||
func TestMnemonicRoundTrip(t *testing.T) {
|
||||
m, err := NewMnemonic()
|
||||
if err != nil {
|
||||
t.Fatalf("NewMnemonic: %v", err)
|
||||
}
|
||||
if !ValidateMnemonic(m) {
|
||||
t.Fatalf("generated mnemonic failed validation: %q", m)
|
||||
}
|
||||
if _, err := deriveIdentity(m); err != nil {
|
||||
t.Fatalf("deriveIdentity(fresh): %v", err)
|
||||
}
|
||||
if ValidateMnemonic("not a real mnemonic at all please") {
|
||||
t.Fatal("garbage phrase validated as a mnemonic")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user