From 126cf2f5c37df17ab50d9bb8f215e6d2147c1399a43d91086154957aa0c08259 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Sun, 26 Apr 2026 01:19:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20SAWD-v1=20=D0=B8=20Arweave-=D0=BA=D0=BE=D1=88=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D0=BA=20=D0=B2=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.properties | 4 +- docs/SHINE_ARWEAVE_DERIVATION_V1.md | 104 ++++ shine-UI/index.html | 4 +- shine-UI/js/pages/wallet-view.js | 554 +++++++++++++----- .../js/services/arweave-wallet-service.js | 173 ++++++ shine-UI/js/services/device-key-utils.js | 51 ++ shine-UI/js/services/sawd-v1.js | 447 ++++++++++++++ shine-UI/js/services/solana-wallet-service.js | 12 +- 8 files changed, 1174 insertions(+), 175 deletions(-) create mode 100644 docs/SHINE_ARWEAVE_DERIVATION_V1.md create mode 100644 shine-UI/js/services/arweave-wallet-service.js create mode 100644 shine-UI/js/services/device-key-utils.js create mode 100644 shine-UI/js/services/sawd-v1.js diff --git a/VERSION.properties b/VERSION.properties index a807936..eb33f7c 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.3 -server.version=1.2.3 +client.version=1.2.4 +server.version=1.2.4 diff --git a/docs/SHINE_ARWEAVE_DERIVATION_V1.md b/docs/SHINE_ARWEAVE_DERIVATION_V1.md new file mode 100644 index 0000000..81d50f5 --- /dev/null +++ b/docs/SHINE_ARWEAVE_DERIVATION_V1.md @@ -0,0 +1,104 @@ +# SHiNE Arweave Wallet Derivation v1 + +Сокращение: **SAWD-v1**. + +## Назначение +Из 32-байтного `deviceKey32` пользователя получить один и тот же нативный Arweave RSA-4096 JWK wallet и один и тот же Arweave address. + +## Вход +- `deviceKey32`: ровно 32 байта. +- Если исходный `device.key` хранится как Ed25519 PKCS8 base64, нужно извлечь последние 32 байта из PKCS8. +- Если используется Solana keypair JSON на 64 байта, используются только `bytes[0..31]`. + +## Выход +```json +{ + "derivation": "SAWD-v1", + "jwk": { + "kty": "RSA", + "e": "AQAB", + "n": "...", + "d": "...", + "p": "...", + "q": "...", + "dp": "...", + "dq": "...", + "qi": "..." + }, + "owner": "...", + "address": "..." +} +``` + +Где: +- `owner = jwk.n` +- `address = base64url_no_padding(SHA-256(unsigned_big_endian_bytes(n)))` + +## Константы +- `DERIVATION_NAME = "SAWD-v1"` +- `MASTER_LABEL = "SHINE/ARWEAVE/RSA4096/SAWD-v1/MASTER"` +- `STREAM_LABEL = "SHINE/ARWEAVE/RSA4096/SAWD-v1/STREAM"` +- `MR_LABEL = "SHINE/ARWEAVE/RSA4096/SAWD-v1/MILLER-RABIN"` +- `RSA_BITS = 4096` +- `PRIME_BITS = 2048` +- `PUBLIC_EXPONENT = 65537` +- `MILLER_RABIN_ROUNDS = 64` +- `SMALL_PRIME_LIMIT = 10000` + +## Алгоритм +1. Проверить `deviceKey32.length == 32`. +2. `masterSeed32 = HMAC-SHA256(key = UTF8(MASTER_LABEL), message = deviceKey32)`. +3. Реализовать `deriveBytes(label, length)`: + - `output = empty` + - `counter = 0` + - while `output.length < length`: + - `block = HMAC-SHA256(key = masterSeed32, message = UTF8(STREAM_LABEL) || UTF8("/") || UTF8(label) || UTF8("/") || uint64_be(counter))` + - `output = output || block` + - `counter++` + - вернуть первые `length` байт. +4. Для `p` и `q`: + - `raw = deriveBytes(label + "/" + index, 256)` + - `candidate = unsigned_big_endian_integer(raw)` + - `candidate = candidate OR 2^2047` + - `candidate = candidate OR 1` + - Проверить: + - `bitLength(candidate) == 2048` + - `candidate odd` + - не делится на малые простые `<= 10000` + - `gcd(candidate - 1, 65537) == 1` + - проходит Miller-Rabin `64 rounds` +5. Базы Miller-Rabin детерминированные: + - `baseBytes = HMAC-SHA256(key = masterSeed32, message = UTF8(MR_LABEL) || UTF8("/") || UTF8(label) || UTF8("/") || uint64_be(index) || UTF8("/") || uint32_be(round))` + - `a = 2 + (unsigned_big_endian_integer(baseBytes) mod (candidate - 3))` +6. `p = derivePrime("p")`, `q = derivePrime("q")`. +7. Если `p == q`, продолжить поиск `q`. +8. Если `p > q`, поменять местами. В SAWD-v1 всегда `p < q`. +9. `n = p * q` +10. `e = 65537` +11. `lambda = lcm(p - 1, q - 1)` +12. `d = modular_inverse(e, lambda)` +13. `dp = d mod (p - 1)` +14. `dq = d mod (q - 1)` +15. `qi = modular_inverse(q, p)` +16. Сформировать JWK: + - `kty = "RSA"` + - `e = "AQAB"` + - `n,d,p,q,dp,dq,qi = base64url unsigned big-endian integer without padding` +17. `owner = jwk.n` +18. `address = base64url_no_padding(SHA-256(unsigned_big_endian_bytes(n)))` + +## Запрещено +- `crypto.generateKeyPair` +- `WebCrypto generateKey` +- `KeyPairGenerator` +- `SecureRandom(seed)` +- `Math.random` +- системный `random` +- ArDrive CLI +- Turbo +- внешний API для генерации ключа +- сохранение приватного JWK + +## Версионирование стандарта +Если меняется любая константа или шаг алгоритма — это уже **SAWD-v2**. +Пользователи, созданные на SAWD-v1, должны продолжать восстанавливаться через SAWD-v1. diff --git a/shine-UI/index.html b/shine-UI/index.html index 3282b43..5c9d7cf 100644 --- a/shine-UI/index.html +++ b/shine-UI/index.html @@ -6,8 +6,8 @@ Shine UI Demo