Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bitboxsync/engine_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
11 changes: 4 additions & 7 deletions bitboxsync/engine_identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -106,7 +106,6 @@ func TestIdentityIntentHelpersUseTypedIntentMethods(t *testing.T) {
inviteID: inviteID,
inviteServerSecretHash: inviteSecretHash,
expiresAt: 1234,
maxPending: 2,
maxAccepted: 3,
})

Expand Down Expand Up @@ -141,7 +140,6 @@ type identityCall struct {
inviteServerSecretHash []byte
serverOrigin string
expiresAt int64
maxPending int
maxAccepted int
}

Expand Down Expand Up @@ -222,19 +220,18 @@ 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),
namespaceID: bytes.Clone(namespaceID),
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
}
Expand Down
16 changes: 0 additions & 16 deletions bitboxsync/namespace_invites.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand All @@ -109,7 +99,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption
inviteIDRaw,
inviteServerSecretHash[:],
uint64(expiresAt),
maxPending,
maxAccepted,
)
if err != nil {
Expand Down Expand Up @@ -141,7 +130,6 @@ func (n *Namespace) CreateInvite(ctx context.Context, opts NamespaceInviteOption
inviteIDRaw,
inviteServerSecretHash[:],
expiresAt,
opts.MaxPending,
opts.MaxAccepted,
)
if err != nil {
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down
15 changes: 0 additions & 15 deletions bitboxsync/namespace_invites_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func TestCreateInviteReturnsTokenAndSubmitsAction(t *testing.T) {
NamespaceID: namespace.ID(),
InviteID: inviteID,
ExpiresAt: req.ExpiresAt,
MaxPending: req.MaxPending,
MaxAccepted: req.MaxAccepted,
})
default:
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions bitboxsync/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions protocol/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand All @@ -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"`
}

Expand All @@ -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"`
Expand Down
6 changes: 2 additions & 4 deletions protocol/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -480,16 +480,14 @@ 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...)
expiresBytes := make([]byte, 8)
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
Expand Down
13 changes: 5 additions & 8 deletions raw/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -183,7 +181,6 @@ func (d *DummyKeystore) SignCreateNamespaceInviteIntent(_ context.Context, chall
inviteID,
inviteServerSecretHash,
uint64(expiresAt),
uint32(maxPending),
uint32(maxAccepted),
)
if err != nil {
Expand Down