diff --git a/bitboxsync/engine_identity.go b/bitboxsync/engine_identity.go index e9e5984..a48b497 100644 --- a/bitboxsync/engine_identity.go +++ b/bitboxsync/engine_identity.go @@ -21,8 +21,8 @@ func (e *Engine) signRevokeAllTokensIntent(ctx context.Context, challenge []byte return e.identity.SignRevokeAllTokensIntent(ctx, challenge) } -func (e *Engine) signCreateNamespaceInviteIntent(ctx context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxPending, maxAccepted int) ([]byte, error) { - return e.identity.SignCreateNamespaceInviteIntent(ctx, challenge, namespaceID, inviteID, inviteServerSecretHash, expiresAt, maxPending, maxAccepted) +func (e *Engine) signCreateNamespaceInviteIntent(ctx context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxAccepted int) ([]byte, error) { + return e.identity.SignCreateNamespaceInviteIntent(ctx, challenge, namespaceID, inviteID, inviteServerSecretHash, expiresAt, maxAccepted) } func (e *Engine) signNamespaceJoinRequestIntent(ctx context.Context, namespaceID, inviteID []byte, serverOrigin string, expiresAt int64) ([]byte, error) { diff --git a/bitboxsync/engine_identity_test.go b/bitboxsync/engine_identity_test.go index 5b40e90..9988ade 100644 --- a/bitboxsync/engine_identity_test.go +++ b/bitboxsync/engine_identity_test.go @@ -86,9 +86,9 @@ func TestIdentityIntentHelpersUseTypedIntentMethods(t *testing.T) { inviteID := bytes.Repeat([]byte{0x05}, protocol.InviteIDLength) inviteSecretHash := bytes.Repeat([]byte{0x06}, protocol.InviteServerSecretHashLength) createChallenge := bytes.Repeat([]byte{0x07}, 32) - createSignature, err := engine.signCreateNamespaceInviteIntent(ctx, createChallenge, namespaceID, inviteID, inviteSecretHash, 1234, 2, 3) + createSignature, err := engine.signCreateNamespaceInviteIntent(ctx, createChallenge, namespaceID, inviteID, inviteSecretHash, 1234, 3) require.NoError(t, err) - actionFields, err := protocol.CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteSecretHash, 1234, 2, 3) + actionFields, err := protocol.CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteSecretHash, 1234, 3) require.NoError(t, err) wantCreate, err := protocol.SensitiveActionIntent( createChallenge, @@ -106,7 +106,6 @@ func TestIdentityIntentHelpersUseTypedIntentMethods(t *testing.T) { inviteID: inviteID, inviteServerSecretHash: inviteSecretHash, expiresAt: 1234, - maxPending: 2, maxAccepted: 3, }) @@ -141,7 +140,6 @@ type identityCall struct { inviteServerSecretHash []byte serverOrigin string expiresAt int64 - maxPending int maxAccepted int } @@ -222,7 +220,7 @@ func (r *recordingIdentity) SignRevokeAllTokensIntent(_ context.Context, challen return ed25519.Sign(r.authPriv, payload), nil } -func (r *recordingIdentity) SignCreateNamespaceInviteIntent(_ context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxPending, maxAccepted int) ([]byte, error) { +func (r *recordingIdentity) SignCreateNamespaceInviteIntent(_ context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxAccepted int) ([]byte, error) { r.calls = append(r.calls, identityCall{ name: "create-invite", challenge: bytes.Clone(challenge), @@ -230,11 +228,10 @@ func (r *recordingIdentity) SignCreateNamespaceInviteIntent(_ context.Context, c inviteID: bytes.Clone(inviteID), inviteServerSecretHash: bytes.Clone(inviteServerSecretHash), expiresAt: expiresAt, - maxPending: maxPending, maxAccepted: maxAccepted, }) keyID := r.keyID() - actionFields, err := protocol.CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecretHash, uint64(expiresAt), uint32(maxPending), uint32(maxAccepted)) + actionFields, err := protocol.CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecretHash, uint64(expiresAt), uint32(maxAccepted)) if err != nil { return nil, err } diff --git a/bitboxsync/namespace_invites.go b/bitboxsync/namespace_invites.go index 9f386c0..793a05b 100644 --- a/bitboxsync/namespace_invites.go +++ b/bitboxsync/namespace_invites.go @@ -32,15 +32,9 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption if opts.TTL > protocol.MaxInviteTTL { return protocol.NamespaceInviteToken{}, fmt.Errorf("invite ttl exceeds maximum %s", protocol.MaxInviteTTL) } - if opts.MaxPending <= 0 { - opts.MaxPending = protocol.MaxPendingJoinRequestsPerInvite - } if opts.MaxAccepted <= 0 { opts.MaxAccepted = protocol.MaxAcceptedJoinRequestsPerInvite } - if opts.MaxPending > protocol.MaxPendingJoinRequestsPerInvite { - return protocol.NamespaceInviteToken{}, fmt.Errorf("maxPending exceeds maximum %d", protocol.MaxPendingJoinRequestsPerInvite) - } if opts.MaxAccepted > protocol.MaxAcceptedJoinRequestsPerInvite { return protocol.NamespaceInviteToken{}, fmt.Errorf("maxAccepted exceeds maximum %d", protocol.MaxAcceptedJoinRequestsPerInvite) } @@ -79,10 +73,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption return protocol.NamespaceInviteToken{}, err } } - maxPending, err := checkedUint32("maxPending", opts.MaxPending) - if err != nil { - return protocol.NamespaceInviteToken{}, err - } maxAccepted, err := checkedUint32("maxAccepted", opts.MaxAccepted) if err != nil { return protocol.NamespaceInviteToken{}, err @@ -109,7 +99,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption inviteIDRaw, inviteServerSecretHash[:], uint64(expiresAt), - maxPending, maxAccepted, ) if err != nil { @@ -141,7 +130,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption inviteIDRaw, inviteServerSecretHash[:], expiresAt, - opts.MaxPending, opts.MaxAccepted, ) if err != nil { @@ -155,7 +143,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption Challenge: challengeResp.Challenge, IntentSignature: hex.EncodeToString(signature), ExpiresAt: expiresAt, - MaxPending: opts.MaxPending, MaxAccepted: opts.MaxAccepted, }) if err != nil { @@ -164,9 +151,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption if resp.ExpiresAt != expiresAt { return fmt.Errorf("create invite response expiry mismatch: got %d want %d", resp.ExpiresAt, expiresAt) } - if resp.MaxPending != opts.MaxPending { - return fmt.Errorf("create invite response maxPending mismatch: got %d want %d", resp.MaxPending, opts.MaxPending) - } if resp.MaxAccepted != opts.MaxAccepted { return fmt.Errorf("create invite response maxAccepted mismatch: got %d want %d", resp.MaxAccepted, opts.MaxAccepted) } diff --git a/bitboxsync/namespace_invites_test.go b/bitboxsync/namespace_invites_test.go index 91ee8e3..9b6e2e7 100644 --- a/bitboxsync/namespace_invites_test.go +++ b/bitboxsync/namespace_invites_test.go @@ -61,7 +61,6 @@ func TestCreateInviteReturnsTokenAndSubmitsAction(t *testing.T) { NamespaceID: namespace.ID(), InviteID: inviteID, ExpiresAt: req.ExpiresAt, - MaxPending: req.MaxPending, MaxAccepted: req.MaxAccepted, }) default: @@ -145,12 +144,6 @@ func TestCreateInviteRejectsValuesOutsideProtocolPolicy(t *testing.T) { }) require.ErrorContains(t, err, "invite ttl exceeds maximum") - _, err = namespace.CreateInvite(ctx, NamespaceInviteOptions{ - ServerOrigin: "https://sync.example", - MaxPending: protocol.MaxPendingJoinRequestsPerInvite + 1, - }) - require.ErrorContains(t, err, "maxPending exceeds maximum") - _, err = namespace.CreateInvite(ctx, NamespaceInviteOptions{ ServerOrigin: "https://sync.example", MaxAccepted: protocol.MaxAcceptedJoinRequestsPerInvite + 1, @@ -171,13 +164,6 @@ func TestCreateInviteRejectsResponseParameterMismatch(t *testing.T) { }, wantErr: "expiry mismatch", }, - { - name: "max pending", - mutate: func(resp *protocol.CreateNamespaceInviteResponse) { - resp.MaxPending++ - }, - wantErr: "maxPending mismatch", - }, { name: "max accepted", mutate: func(resp *protocol.CreateNamespaceInviteResponse) { @@ -211,7 +197,6 @@ func TestCreateInviteRejectsResponseParameterMismatch(t *testing.T) { NamespaceID: namespace.ID(), InviteID: req.InviteID, ExpiresAt: req.ExpiresAt, - MaxPending: req.MaxPending, MaxAccepted: req.MaxAccepted, } tt.mutate(&resp) diff --git a/bitboxsync/types.go b/bitboxsync/types.go index 0b85c59..9d047ec 100644 --- a/bitboxsync/types.go +++ b/bitboxsync/types.go @@ -110,9 +110,6 @@ type NamespaceInviteOptions struct { InviteSecret string // TTL controls invite lifetime. Non-positive values use Config.InviteTTL. TTL time.Duration - // MaxPending caps active pending join requests for this invite. Non-positive - // values use the protocol default. - MaxPending int // MaxAccepted caps successful first-time approvals through this invite. // Non-positive values use the protocol default. MaxAccepted int diff --git a/protocol/api.go b/protocol/api.go index 1b373b2..29145d9 100644 --- a/protocol/api.go +++ b/protocol/api.go @@ -244,7 +244,6 @@ type CreateNamespaceInviteRequest struct { Challenge string `json:"challenge"` IntentSignature string `json:"intentSignature"` ExpiresAt int64 `json:"expiresAt"` - MaxPending int `json:"maxPending"` MaxAccepted int `json:"maxAccepted"` } @@ -253,7 +252,6 @@ type CreateNamespaceInviteResponse struct { NamespaceID string `json:"namespaceId"` InviteID string `json:"inviteId"` ExpiresAt int64 `json:"expiresAt"` - MaxPending int `json:"maxPending"` MaxAccepted int `json:"maxAccepted"` } @@ -263,7 +261,6 @@ type NamespaceInviteSummary struct { CreatedByKeyID string `json:"createdByKeyId"` CreatedAt time.Time `json:"createdAt"` ExpiresAt int64 `json:"expiresAt"` - MaxPending int `json:"maxPending"` MaxAccepted int `json:"maxAccepted"` ActiveRequestCount int `json:"activeRequestCount"` AcceptedCount int `json:"acceptedCount"` diff --git a/protocol/crypto.go b/protocol/crypto.go index 307a290..b62581c 100644 --- a/protocol/crypto.go +++ b/protocol/crypto.go @@ -470,7 +470,7 @@ func SensitiveActionIntent(challenge []byte, action, kind string, keyID, actionF // CreateNamespaceInviteActionFields builds the canonical sensitive-action // action fields for namespace invite creation. -func CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt uint64, maxPending, maxAccepted uint32) ([]byte, error) { +func CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt uint64, maxAccepted uint32) ([]byte, error) { if len(namespaceID) != NamespaceIDLength { return nil, ErrInvalidNamespaceID } @@ -480,7 +480,7 @@ func CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecret if len(inviteServerSecretHash) != InviteServerSecretHashLength { return nil, fmt.Errorf("invite server secret hash must be %d bytes", InviteServerSecretHashLength) } - buf := make([]byte, 0, NamespaceIDLength+InviteIDLength+InviteServerSecretHashLength+8+4+4) + buf := make([]byte, 0, NamespaceIDLength+InviteIDLength+InviteServerSecretHashLength+8+4) buf = append(buf, namespaceID...) buf = append(buf, inviteID...) buf = append(buf, inviteServerSecretHash...) @@ -488,8 +488,6 @@ func CreateNamespaceInviteActionFields(namespaceID, inviteID, inviteServerSecret binary.BigEndian.PutUint64(expiresBytes, expiresAt) buf = append(buf, expiresBytes...) countBytes := make([]byte, 4) - binary.BigEndian.PutUint32(countBytes, maxPending) - buf = append(buf, countBytes...) binary.BigEndian.PutUint32(countBytes, maxAccepted) buf = append(buf, countBytes...) return buf, nil diff --git a/raw/identity.go b/raw/identity.go index 1c5f540..7ae26cf 100644 --- a/raw/identity.go +++ b/raw/identity.go @@ -52,9 +52,9 @@ type Identity interface { SignRevokeAllTokensIntent(ctx context.Context, challenge []byte) ([]byte, error) // SignCreateNamespaceInviteIntent signs the canonical sensitive-action // intent for namespace invite creation. Hardware implementations must - // display the namespace fingerprint, invite fingerprint, expiry, and invite - // limits being approved. - SignCreateNamespaceInviteIntent(ctx context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxPending, maxAccepted int) ([]byte, error) + // display the namespace fingerprint, invite fingerprint, expiry, and accepted + // member limit being approved. + SignCreateNamespaceInviteIntent(ctx context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxAccepted int) ([]byte, error) // SignNamespaceJoinRequestIntent signs the canonical join-request payload // for a scanned namespace invite. Hardware implementations must display the // server origin, namespace fingerprint, invite fingerprint, and expiry being @@ -168,13 +168,11 @@ func (d *DummyKeystore) SignRevokeAllTokensIntent(_ context.Context, challenge [ // SignCreateNamespaceInviteIntent signs the namespace-invite sensitive action // for the dummy keystore. -func (d *DummyKeystore) SignCreateNamespaceInviteIntent(_ context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxPending, maxAccepted int) ([]byte, error) { +func (d *DummyKeystore) SignCreateNamespaceInviteIntent(_ context.Context, challenge, namespaceID, inviteID, inviteServerSecretHash []byte, expiresAt int64, maxAccepted int) ([]byte, error) { if expiresAt < 0 { return nil, fmt.Errorf("namespace invite expiry before unix epoch") } - if maxPending < 0 || maxAccepted < 0 || - maxPending > protocol.MaxPendingJoinRequestsPerInvite || - maxAccepted > protocol.MaxAcceptedJoinRequestsPerInvite { + if maxAccepted < 0 || maxAccepted > protocol.MaxAcceptedJoinRequestsPerInvite { return nil, fmt.Errorf("namespace invite limits are out of range") } keyID := d.keyID() @@ -183,7 +181,6 @@ func (d *DummyKeystore) SignCreateNamespaceInviteIntent(_ context.Context, chall inviteID, inviteServerSecretHash, uint64(expiresAt), - uint32(maxPending), uint32(maxAccepted), ) if err != nil {