diff --git a/Documentation/Compatibility/BRC-Matrix.md b/Documentation/Compatibility/BRC-Matrix.md index 289e003..f72f73f 100644 --- a/Documentation/Compatibility/BRC-Matrix.md +++ b/Documentation/Compatibility/BRC-Matrix.md @@ -38,7 +38,7 @@ | BRC-75 `key-derivation/0075.md` | BIP-39 entropy/mnemonic/checksum/seed and wordlists. | **Full adapter**: `compat/bip39` and language wordlists. | `BSVCompat` | SHA256, PBKDF2-HMAC-SHA512, Unicode normalization. | Use official BIP-39 vectors; BRC contains no comprehensive fixed vectors. | External-standard dependency and wordlist provenance must be tracked. | BRC-42 `key-derivation/0042.md` | BKDS: shared point = sender private × recipient public; HMAC-SHA256(invoice UTF-8) keyed by compressed shared point gives big-endian scalar; child public = recipient root + hG; child private = recipient root private + h mod n. | **Full**: `ec.PrivateKey.DeriveChild`, `ec.PublicKey.DeriveChild`; `wallet.KeyDeriver`. | `BSVKeys` | SEC1 compressed keys, secp256k1 scalar/point arithmetic, HMAC-SHA256, UTF-8. | Five private and five public derivation vectors in BRC, mirrored in Go testdata. | Must specify rejection/retry for invalid zero/out-of-range tweaks even if vectors do not hit them. | BRC-43 `key-derivation/0043.md` | Invoice `--` UTF-8; security levels 0/1/2; counterparty can be self, anyone (private scalar 1), or public key. | **Full with newer-limit behavior**: `wallet.KeyDeriver.computeInvoiceNumber` and protocol/keyID validation. | `BSVKeys` value types, `BSVWallet` policy | BRC-42; normalized UTF-8 and length validation. | Examples throughout; Go key-deriver tests. | Direct limit conflict: BRC-43 prose allows protocol up to 280 characters and keyID 1033 bytes, while BRC-100/current Go enforce protocol 5–400 characters and keyID ≤800 characters. For Go parity use 400/800 as an explicit compatibility profile; seek normative precedence clarification. -| BRC-44 `key-derivation/0044.md` | Reserve any protocol ID beginning with literal `admin` for client-internal use at every security level; reject application requests. | **Partial** through wallet protocol validation/policy. | `BSVWallet` policy, shared protocol identifier type in `BSVKeys` | BRC-43. | Simple prefix rule. | Define case sensitivity and normalization ordering explicitly; document reads as case-sensitive literal prefix. +| BRC-44 `key-derivation/0044.md` | Reserve any protocol ID beginning with literal `admin` for client-internal use at every security level; reject application requests. | **Partial** through wallet protocol validation/policy. The exact WPM `[2, "admin metadata encryption"]` identifier is exposed only as the explicit trusted-local `WalletProtocolID.walletMetadataEncryption` constant; generic construction and application JSON/wire encoding and decoding reject it. | `BSVWallet` policy, shared protocol identifier type in `BSVKeys` | BRC-43. | Simple prefix rule plus exact local-field, ProtoWallet vector, and application-boundary rejection tests. | Define case sensitivity and normalization ordering explicitly; document reads as case-sensitive literal prefix. Representation by the named constant is not authorization, so wallet origin/policy gates still apply. | BRC-69/72/93/94/97 `key-derivation/{0069,0072,0093,0094}.md`, `wallet/0097.md` | Reveal full counterparty shared secret or a specific linkage HMAC (69); protect linkage in transit (72); acknowledge BRC-69 proof limitations (93); Schnorr/DLEQ proof `(R,S′,z)` with challenge `SHA256(compressed A||B||S||S′||R) mod n` and two verification equations (94); prefix extensible proof type (97). | **Full/partial**: `wallet.ProtoWallet.RevealCounterpartyKeyLinkage`, `RevealSpecificKeyLinkage`; `primitives/schnorr.Schnorr.GenerateProof`, `VerifyProof`. Go currently emits proof type byte `0`. | `BSVKeys`, `BSVCrypto`, `BSVWallet` | BRC-42/43, EC point operations, HMAC/SHA256, authenticated encryption for 72. | Algebraic examples and Go tests; no broad malformed-proof corpus. | Proof-type registry/evolution and strict point/scalar validation need a policy; BRC-69 should not be used as proof when BRC-94 is required. | BRC-84 `key-derivation/0084.md` | Linked derivation using HMAC keyed by serialized counterparty public key rather than ECDH shared secret. | **None direct** | `BSVKeys` optional derivation mode | BRC-42/43 primitives. | Inline examples. | Security/domain-separation choice; do not silently fold into Type-42. | BRC-86 `key-derivation/0086.md` | Bidirectionally authenticated/restricted Type-42 setup and privacy controls. | **None direct** | `BSVAuth` + `BSVKeys` | BRC-42/43 and peer authentication. | Protocol walkthrough. | Higher-layer handshake, not primitive derivation parity. diff --git a/Documentation/Compatibility/Rulings.md b/Documentation/Compatibility/Rulings.md index 0f15625..5dca17c 100644 --- a/Documentation/Compatibility/Rulings.md +++ b/Documentation/Compatibility/Rulings.md @@ -13,7 +13,7 @@ behind the general rule. | COMP-001 | CompactSize decoding in Go accepts non-minimal forms and some callers use max-u64 as “-1”. | Provide bounded permissive and canonical-required decoding policies. Encode canonically. Model max-u64 absence as a named sentinel only in formats that specify it; never as an ordinary length. | Preserves wire import compatibility without allowing ambiguous canonical output or unbounded allocation. Test every prefix boundary, truncation, non-minimal form, max-u64, and per-call maximum. | | COMP-002 | BRC-2 prose describes a shared-secret hash input that disagrees with the published vectors and Go. | Follow the published vectors and Go v1.3.3 padded-x-coordinate derivation. Mark the prose conflict and do not offer a second silent variant. | The vectors are interoperable evidence. Test exact ciphertext, plaintext, HMAC, wrong counterparty/protocol/key ID, tag tampering, and IV/tag truncation. | | COMP-003 | BRC-30 says four-byte source satoshis; Go and Bitcoin amounts use eight bytes. | Parse and emit unsigned 64-bit little-endian source amounts. Do not emit the ambiguous four-byte form. | Matches Go Extended Format and the transaction amount domain. Test Go↔Swift bytes and overflow/truncation. | -| COMP-004 | BRC-43 gives 280/1033 limits; BRC-100 and Go use 400/800. Go's Unicode-aware trimming and lowercasing can also admit names outside a stable ASCII derivation profile. | Use protocol names of 5…400 UTF-8 bytes and key IDs of 1…800 UTF-8 bytes. Protocol names trim ASCII whitespace, lowercase ASCII, allow only letters, digits, and single spaces, reject the `admin` prefix and ` protocol` suffix, and do not normalize key IDs. | Newer wallet limits and the parity baseline agree. The stricter Swift identifier policy fails closed rather than deriving a different key from Unicode or reserved-name artifacts. Tests cover byte boundaries, every ASCII character, Unicode rejection, reserved names, and distinct composed/decomposed key IDs. | +| COMP-004 | BRC-43 gives 280/1033 limits; BRC-100 and Go use 400/800. Go's Unicode-aware trimming and lowercasing can also admit names outside a stable ASCII derivation profile. BRC-44 reserves `admin` protocols for trusted wallet internals. | Use protocol names of 5…400 UTF-8 bytes and key IDs of 1…800 UTF-8 bytes. Protocol names trim ASCII whitespace, lowercase ASCII, allow only letters, digits, and single spaces, reject the `admin` prefix and ` protocol` suffix, and do not normalize key IDs. The one wallet-internal WPM metadata identifier is available only as the explicit `WalletProtocolID.walletMetadataEncryption` constant; generic construction and application JSON/wire encoding and decoding continue to reject it. | Newer wallet limits and the parity baseline agree. The stricter Swift identifier policy fails closed rather than deriving a different key from Unicode or reserved-name artifacts. The named constant makes the wallet-internal exception auditable without widening external encoders or decoders. Tests cover byte boundaries, every ASCII character, Unicode rejection, reserved names, exact local fields and ProtoWallet output, boundary rejection, and distinct composed/decomposed key IDs. | | COMP-005 | BRC-48 specifies lock-after PushDrop; Go creates both but decodes only lock-before. | Decode and construct spec-conformant lock-after. Support lock-before through an explicitly named compatibility mode. | Achieves BRC conformance without losing Go ecosystem inputs. Test that no auto-detection ambiguity changes pushed data. | | COMP-006 | BRC-74 pseudocode can be read as rejecting offset zero. | Accept offset/index zero. | The binary model and Go behavior require zero. Use independently authored zero-index cases and the pinned external oracle; do not copy the Open BSV example into the MIT fixture bundle under the current license policy. | | COMP-007 | A Go comment associates BEEF v1 with BRC-64. | Treat BRC-62 as authoritative for BEEF v1. | The comment is non-functional and contradicts the specification registry. | diff --git a/Sources/BSVWallet/Values/WalletIdentifiers.swift b/Sources/BSVWallet/Values/WalletIdentifiers.swift index 6f71ba9..0367d20 100644 --- a/Sources/BSVWallet/Values/WalletIdentifiers.swift +++ b/Sources/BSVWallet/Values/WalletIdentifiers.swift @@ -49,6 +49,13 @@ public struct WalletProtocolID: Hashable, Codable, Sendable { public let securityLevel: WalletSecurityLevel public let name: String + /// Canonical wallet-internal protocol used by WalletPermissionsManager metadata encryption. + /// This represents the identifier only; callers must still enforce authorization policy. + public static let walletMetadataEncryption = WalletProtocolID( + securityLevel: .everyAppAndCounterparty, + canonicalName: "admin metadata encryption" + ) + public init(securityLevel: WalletSecurityLevel, name: String) throws { let normalized = try Self.canonicalName(name) guard !normalized.hasPrefix("admin") else { diff --git a/Tests/BSVWalletTests/ProtoWalletTests.swift b/Tests/BSVWalletTests/ProtoWalletTests.swift index b8cd233..fcbd0c9 100644 --- a/Tests/BSVWalletTests/ProtoWalletTests.swift +++ b/Tests/BSVWalletTests/ProtoWalletTests.swift @@ -64,10 +64,7 @@ final class ProtoWalletTests: XCTestCase { func testWalletInternalAdminMetadataEncryptionVector() async throws { let root = try walletTestPrivateKey(42) - let protocolID = try WalletProtocolID.walletInternalAdmin( - securityLevel: .everyAppAndCounterparty, - name: "admin metadata encryption" - ) + let protocolID = WalletProtocolID.walletMetadataEncryption let keyID = try WalletKeyID("1") let nonce = (0..<32).map { UInt8($0) } let plaintext = Array("Yours Wallet metadata".utf8) diff --git a/Tests/BSVWalletTests/WalletIdentifierTests.swift b/Tests/BSVWalletTests/WalletIdentifierTests.swift index 2b94985..e761412 100644 --- a/Tests/BSVWalletTests/WalletIdentifierTests.swift +++ b/Tests/BSVWalletTests/WalletIdentifierTests.swift @@ -60,12 +60,14 @@ final class WalletIdentifierTests: XCTestCase { } func testWalletInternalAdminProtocolsStayOffExternalBoundaries() throws { - let value = try WalletProtocolID.walletInternalAdmin( + let factoryValue = try WalletProtocolID.walletInternalAdmin( securityLevel: .everyAppAndCounterparty, name: " \tAdMiN Metadata Encryption\n" ) + let value = WalletProtocolID.walletMetadataEncryption XCTAssertEqual(value.securityLevel, .everyAppAndCounterparty) XCTAssertEqual(value.name, "admin metadata encryption") + XCTAssertEqual(value, factoryValue) XCTAssertThrowsError(try WalletProtocolID( securityLevel: .everyAppAndCounterparty, @@ -106,6 +108,20 @@ final class WalletIdentifierTests: XCTestCase { .nonRoundTrippableValue(kind: "BRC-44 wallet-internal protocol identifier") ) } + + var rawAdminProtocol = WalletWireWriter() + rawAdminProtocol.writeByte(WalletSecurityLevel.everyAppAndCounterparty.rawValue) + try rawAdminProtocol.writeString("admin metadata encryption") + var rawReader = WalletWireReader(rawAdminProtocol.bytes) + XCTAssertThrowsError(try walletWireDecodeProtocol( + from: &rawReader, + limits: .standard + )) { error in + XCTAssertEqual( + error as? WalletWireError, + .nonRoundTrippableValue(kind: "protocol identifier") + ) + } } func testSpecificLinkageRevelationHasTheOnlyExtendedProtocolLimit() throws {