1// Copyright 2016 Keybase Inc. All rights reserved.
2// Use of this source code is governed by a BSD
3// license that can be found in the LICENSE file.
4
5package kbfsmd
6
7import (
8	"encoding"
9	"encoding/hex"
10	"errors"
11
12	"github.com/keybase/client/go/kbfs/kbfscrypto"
13)
14
15const (
16	// BranchIDByteLen is the number of bytes in a per-device per-TLF branch ID.
17	BranchIDByteLen = 16
18	// BranchIDStringLen is the number of characters in the string
19	// representation of a per-device per-TLF branch ID.
20	BranchIDStringLen = 2 * BranchIDByteLen
21)
22
23// BranchID encapsulates a per-device per-TLF branch ID.
24type BranchID struct {
25	id [BranchIDByteLen]byte
26}
27
28var _ encoding.BinaryMarshaler = (*BranchID)(nil)
29var _ encoding.BinaryUnmarshaler = (*BranchID)(nil)
30
31// NullBranchID is an empty BranchID
32var NullBranchID = BranchID{}
33
34// PendingLocalSquashBranchID indicates a local branch that is not in known
35// conflict with the master branch, but just needs to be squashed locally.
36var PendingLocalSquashBranchID = BranchID{
37	[BranchIDByteLen]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}
38
39// Bytes returns the bytes of the BranchID.
40func (id BranchID) Bytes() []byte {
41	return id.id[:]
42}
43
44// String implements the Stringer interface for BranchID.
45func (id BranchID) String() string {
46	return hex.EncodeToString(id.id[:])
47}
48
49// MarshalBinary implements the encoding.BinaryMarshaler interface for BranchID.
50func (id BranchID) MarshalBinary() (data []byte, err error) {
51	return id.id[:], nil
52}
53
54// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
55// for BranchID.
56func (id *BranchID) UnmarshalBinary(data []byte) error {
57	if len(data) != BranchIDByteLen {
58		return errors.New("invalid BranchID")
59	}
60	copy(id.id[:], data)
61	return nil
62}
63
64// ParseBranchID parses a hex encoded BranchID. Returns NullBranchID
65// and an InvalidBranchID on falire.
66func ParseBranchID(s string) (BranchID, error) {
67	if len(s) != BranchIDStringLen {
68		return NullBranchID, InvalidBranchID{s}
69	}
70	bytes, err := hex.DecodeString(s)
71	if err != nil {
72		return NullBranchID, InvalidBranchID{s}
73	}
74	var id BranchID
75	err = id.UnmarshalBinary(bytes)
76	if err != nil {
77		return NullBranchID, InvalidBranchID{s}
78	}
79	return id, nil
80}
81
82// MakeRandomBranchID generates a per-device branch ID using a CSPRNG.
83// It will not return LocalSquashBranchID or NullBranchID.
84func MakeRandomBranchID() (BranchID, error) {
85	var id BranchID
86	// Loop just in case we randomly pick the null or local squash
87	// branch IDs.
88	for id == NullBranchID || id == PendingLocalSquashBranchID {
89		err := kbfscrypto.RandRead(id.id[:])
90		if err != nil {
91			return BranchID{}, err
92		}
93	}
94	return id, nil
95}
96
97// FakeBranchID creates a fake branch ID from the given byte.
98func FakeBranchID(b byte) BranchID {
99	bytes := [BranchIDByteLen]byte{b}
100	return BranchID{bytes}
101}
102