├── README.md ├── primitives.go ├── primitives_test.go ├── proto.go ├── proto_test.go ├── tree.go └── tree_test.go /README.md: -------------------------------------------------------------------------------- 1 | treekeys 2 | ======== 3 | 4 | An attempt to implement the [tree-based group keying 5 | scheme](https://eprint.iacr.org/2017/666.pdf) described by Cohn-Gordon, et al. 6 | 7 | ## Quickstart 8 | 9 | ``` 10 | > go get github.com/bifurcation/treekeys 11 | > go test github.com/bifurcation/treekeys -run Performance -v 12 | ``` 13 | 14 | ## Performance 15 | 16 | On my MacBook Pro: 17 | 18 | ``` 19 | Nodes Setup AckSetup Update AckUpdate 20 | ============================================ 21 | 3 1 0 0 0 22 | 7 4 0 0 0 23 | 127 115 0 0 0 24 | 1023 1067 0 1 0 25 | 32767 41825 6 7 8 26 | 131071 183468 41 20 19 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /primitives.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/rand" 6 | "crypto/sha256" 7 | "encoding/base64" 8 | "encoding/json" 9 | "golang.org/x/crypto/curve25519" 10 | ) 11 | 12 | func ceillog2(n int) int { 13 | l := 0 14 | for n > (1 << uint(l)) { 15 | l += 1 16 | } 17 | return l 18 | } 19 | 20 | func pow2(n int) int { 21 | m := 1 22 | for m < n { 23 | m <<= 1 24 | } 25 | return m >> 1 26 | } 27 | 28 | type PrivateKey [32]byte 29 | 30 | type GroupElement [32]byte 31 | 32 | func (g GroupElement) MarshalJSON() ([]byte, error) { 33 | return json.Marshal(base64.RawURLEncoding.EncodeToString(g[:])) 34 | } 35 | 36 | func (g *GroupElement) UnmarshalJSON(data []byte) error { 37 | var str string 38 | err := json.Unmarshal(data, &str) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | buf, err := base64.RawURLEncoding.DecodeString(str) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | copy((*g)[:], buf) 49 | return nil 50 | } 51 | 52 | func MAC(key, message []byte) []byte { 53 | h := hmac.New(sha256.New, key) 54 | h.Write(message) 55 | return h.Sum(nil) 56 | } 57 | 58 | func VerifyMAC(key, message, mac []byte) bool { 59 | return hmac.Equal(MAC(key, message), mac) 60 | } 61 | 62 | // TODO: Use a real KDF and/or structure inputs better 63 | func KDF(vals ...[]byte) (out PrivateKey) { 64 | h := sha256.New() 65 | for _, val := range vals { 66 | h.Write(val[:]) 67 | } 68 | h.Sum(out[:]) 69 | return 70 | } 71 | 72 | // XXX: This could just be the identity function, but let's add some hashing 73 | // just to keep things interesting 74 | func ι(element GroupElement) PrivateKey { 75 | return KDF(element[:]) 76 | } 77 | 78 | func PK(priv PrivateKey) (pub GroupElement) { 79 | curve25519.ScalarBaseMult((*[32]byte)(&pub), (*[32]byte)(&priv)) 80 | return 81 | } 82 | 83 | func Exp(pub GroupElement, priv PrivateKey) (out GroupElement) { 84 | curve25519.ScalarMult((*[32]byte)(&out), (*[32]byte)(&priv), (*[32]byte)(&pub)) 85 | return 86 | } 87 | 88 | func DHKeyGen() (priv PrivateKey) { 89 | rand.Read(priv[:]) 90 | return 91 | } 92 | 93 | func KeyExchangeKeyGen() PrivateKey { 94 | return DHKeyGen() 95 | } 96 | 97 | // XXX: Assuming something 3DH-like 98 | func KeyExchange(originator bool, ikA PrivateKey, IKB GroupElement, ekA PrivateKey, EKB GroupElement) PrivateKey { 99 | iAB := Exp(EKB, ikA) 100 | iBA := Exp(IKB, ekA) 101 | eAB := Exp(EKB, ekA) 102 | 103 | if !originator { 104 | iAB, iBA = iBA, iAB 105 | } 106 | 107 | return KDF(iAB[:], iBA[:], eAB[:]) 108 | } 109 | -------------------------------------------------------------------------------- /primitives_test.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestGroupElementJSON(t *testing.T) { 9 | g := GroupElement{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 10 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 11 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 12 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37} 13 | 14 | gj, err := json.Marshal(g) 15 | if err != nil { 16 | t.Fatalf("Marshal %v", err) 17 | } 18 | 19 | var g2 GroupElement 20 | err = json.Unmarshal(gj, &g2) 21 | if err != nil { 22 | t.Fatalf("Unmarshal %v", err) 23 | } 24 | 25 | if g != g2 { 26 | t.Fatalf("Mismatch [%x] != [%x]", g, g2) 27 | } 28 | } 29 | 30 | func TestMAC(t *testing.T) { 31 | key := []byte{0, 1, 2, 3} 32 | msg := []byte{4, 5, 6, 7} 33 | mac := MAC(key, msg) 34 | ver := VerifyMAC(key, msg, mac) 35 | if !ver { 36 | t.Fatalf("Verify failure") 37 | } 38 | } 39 | 40 | func TestDH(t *testing.T) { 41 | privA := DHKeyGen() 42 | privB := DHKeyGen() 43 | privC := DHKeyGen() 44 | 45 | gAB := Exp(PK(privA), privB) 46 | gBA := Exp(PK(privB), privA) 47 | if gAB != gBA { 48 | t.Fatalf("gAB != gBA") 49 | } 50 | 51 | // Should we have a better test of ι? Does it matter? 52 | kAB := ι(gAB) 53 | kBA := ι(gBA) 54 | gABC := Exp(PK(privC), kAB) 55 | gBAC := Exp(PK(privC), kBA) 56 | if gABC != gBAC { 57 | t.Fatalf("gABC != gBAC") 58 | } 59 | } 60 | 61 | func TestKeyExchange(t *testing.T) { 62 | ikA := KeyExchangeKeyGen() 63 | ekA := KeyExchangeKeyGen() 64 | ikB := KeyExchangeKeyGen() 65 | ekB := KeyExchangeKeyGen() 66 | 67 | gAB := KeyExchange(true, ikA, PK(ikB), ekA, PK(ekB)) 68 | gBA := KeyExchange(false, ikB, PK(ikA), ekB, PK(ekA)) 69 | if gAB != gBA { 70 | t.Fatalf("gAB != gBA") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /proto.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Endpoint struct { 9 | identityKey PrivateKey 10 | preKeys map[GroupElement]PrivateKey 11 | 12 | // State for a group 13 | roster []GroupElement 14 | copath []GroupElement 15 | λ PrivateKey 16 | tk PrivateKey 17 | } 18 | 19 | func NewEndpoint() *Endpoint { 20 | return &Endpoint{ 21 | identityKey: DHKeyGen(), 22 | preKeys: map[GroupElement]PrivateKey{}, 23 | } 24 | } 25 | 26 | func (e *Endpoint) PreKey() GroupElement { 27 | ek := DHKeyGen() 28 | EK := PK(ek) 29 | e.preKeys[EK] = ek 30 | return EK 31 | } 32 | 33 | func (e Endpoint) Identity() GroupElement { 34 | return PK(e.identityKey) 35 | } 36 | 37 | ///// 38 | 39 | type SetupMessage struct { 40 | I int 41 | ID []GroupElement 42 | EK GroupElement 43 | Ks GroupElement 44 | P []GroupElement 45 | } 46 | 47 | type UpdateMessage struct { 48 | J int 49 | U []GroupElement 50 | } 51 | 52 | type MACMessage struct { 53 | // XXX Should probably have an indicator of message type that is covered by the MAC 54 | Message []byte 55 | MAC []byte 56 | } 57 | 58 | func NewMACMessage(key []byte, msg interface{}) (*MACMessage, error) { 59 | var err error 60 | macmsg := &MACMessage{} 61 | 62 | macmsg.Message, err = json.Marshal(msg) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | macmsg.MAC = MAC(key[:], macmsg.Message) 68 | return macmsg, nil 69 | } 70 | 71 | func (macmsg MACMessage) Verify(key []byte) bool { 72 | return VerifyMAC(key, macmsg.Message, macmsg.MAC) 73 | } 74 | 75 | func (macmsg MACMessage) ToSetupMessage() (*SetupMessage, error) { 76 | var msg SetupMessage 77 | err := json.Unmarshal(macmsg.Message, &msg) 78 | return &msg, err 79 | } 80 | 81 | func (macmsg MACMessage) ToUpdateMessage() (*UpdateMessage, error) { 82 | var msg UpdateMessage 83 | err := json.Unmarshal(macmsg.Message, &msg) 84 | return &msg, err 85 | } 86 | 87 | ///// 88 | 89 | type GroupState struct { 90 | i int 91 | ik PrivateKey 92 | λ PrivateKey 93 | ID []GroupElement 94 | P []GroupElement 95 | tk PrivateKey 96 | sk PrivateKey 97 | } 98 | 99 | func (e *Endpoint) SetupGroup(peers []*Endpoint) (*GroupState, []*MACMessage) { 100 | nPeers := len(peers) 101 | 102 | π := &GroupState{} 103 | π.ik = e.identityKey 104 | π.i = 0 105 | 106 | π.λ = DHKeyGen() 107 | ks := KeyExchangeKeyGen() 108 | 109 | IK := make([]GroupElement, nPeers) 110 | EK := make([]GroupElement, nPeers) 111 | λ := make([]PrivateKey, nPeers) 112 | for i, peer := range peers { 113 | IK[i] = peer.Identity() 114 | EK[i] = peer.PreKey() 115 | λ[i] = ι(PK(KeyExchange(true, π.ik, IK[i], ks, EK[i]))) 116 | } 117 | 118 | T := CreateTree(append([]PrivateKey{π.λ}, λ...)) 119 | π.ID = append([]GroupElement{PK(π.ik)}, IK...) 120 | 121 | m := make([]*MACMessage, nPeers) 122 | for i := range peers { 123 | sm := SetupMessage{ 124 | I: i + 1, 125 | ID: π.ID, 126 | EK: EK[i], 127 | Ks: PK(ks), 128 | P: Copath(T, i+1), 129 | } 130 | 131 | // XXX Ignoring error 132 | m[i], _ = NewMACMessage(λ[i][:], sm) 133 | } 134 | 135 | π.tk = T.Value 136 | π.P = Copath(T, 0) 137 | 138 | // XXX How should π.sk be initialized on group creation? This just assumes 139 | // it is set to the all-zero vector, and combined with the π.sk immediately. 140 | π.DeriveStageKey() 141 | 142 | return π, m 143 | } 144 | 145 | func (e *Endpoint) ProcessSetupMessage(macmsg *MACMessage) *GroupState { 146 | π := &GroupState{} 147 | π.ik = e.identityKey 148 | 149 | // XXX Paper doesn't specify verifying MAC 150 | // XXX Ignoring error 151 | msg, _ := macmsg.ToSetupMessage() 152 | 153 | ek, ok := e.preKeys[msg.EK] 154 | if !ok { 155 | // TODO: Handle this better 156 | panic("Unknown PreKey") 157 | } 158 | 159 | π.λ = ι(PK(KeyExchange(false, π.ik, msg.ID[0], ek, msg.Ks))) 160 | if !macmsg.Verify(π.λ[:]) { 161 | panic("Bad MAC") 162 | } 163 | 164 | // XXX Paper doesn't specify doing anything with i 165 | π.i = msg.I 166 | π.ID = msg.ID 167 | π.P = msg.P 168 | 169 | // XXX PK conversion differs from paper 170 | nks := PathNodeKeys(π.λ, π.P) 171 | π.tk = nks[0] 172 | 173 | // XXX How should π.sk be initialized on group creation? This just assumes 174 | // it is set to the all-zero vector, and combined with the π.sk immediately. 175 | π.DeriveStageKey() 176 | 177 | return π 178 | } 179 | 180 | func (π *GroupState) UpdateKey() *MACMessage { 181 | π.λ = DHKeyGen() 182 | nks := PathNodeKeys(π.λ, π.P) 183 | 184 | // XXX Not computing MAC because it will never be verified 185 | // XXX π.sk is used as the MAC key but never computed 186 | um := UpdateMessage{ 187 | J: π.i, 188 | U: make([]GroupElement, len(π.P)), 189 | } 190 | for i, nk := range nks { 191 | if i == 0 { 192 | continue 193 | } 194 | 195 | um.U[i-1] = PK(nk) 196 | } 197 | 198 | // XXX Ignoring error 199 | mm, _ := NewMACMessage(π.sk[:], um) 200 | 201 | // XXX Assuming stage key update happens every time the tree key changes? 202 | π.tk = nks[0] 203 | π.DeriveStageKey() 204 | 205 | return mm 206 | } 207 | 208 | // XXX This is completely upside-down compared to the paper. I think the paper 209 | // is just wrong here; it acts as if the node-key and copath list were ordered 210 | // starting from the leaves. They actually start from the root, so we need to 211 | // count down from the root, instead of starting with the height and 212 | // decrementing to the right place. 213 | func IndexToUpdate(d, n, i, j int) int { 214 | if i == j { 215 | panic(fmt.Sprintf("Equal indices passed to IndexToUpdate [%d] [%d]", i, j)) 216 | } 217 | 218 | m := pow2(n) 219 | 220 | switch { 221 | case i < m && j < m: 222 | return IndexToUpdate(d+1, m, i, j) 223 | case i >= m && j >= m: 224 | return IndexToUpdate(d+1, n-m, i-m, j-m) 225 | } 226 | 227 | return d 228 | } 229 | 230 | func (π *GroupState) ProcessUpdateMessage(macmsg *MACMessage) { 231 | if !macmsg.Verify(π.sk[:]) { 232 | panic("Bad MAC") 233 | } 234 | 235 | // XXX Ignoring error 236 | msg, _ := macmsg.ToUpdateMessage() 237 | 238 | d := IndexToUpdate(0, len(π.ID), π.i, msg.J) 239 | 240 | π.P[d] = msg.U[d] 241 | nks := PathNodeKeys(π.λ, π.P) 242 | π.tk = nks[0] 243 | 244 | // XXX Assuming this happens every time the tree key changes? 245 | π.DeriveStageKey() 246 | 247 | return 248 | } 249 | 250 | func (π *GroupState) DeriveStageKey() { 251 | idBytes := []byte{} 252 | for _, id := range π.ID { 253 | idBytes = append(idBytes, id[:]...) 254 | } 255 | 256 | π.sk = KDF(π.sk[:], π.tk[:], idBytes) 257 | } 258 | -------------------------------------------------------------------------------- /proto_test.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | import ( 4 | //"fmt" 5 | "reflect" 6 | "testing" 7 | //"time" 8 | ) 9 | 10 | func TestPerformance(t *testing.T) { 11 | // Uncomment for performance test 12 | /* 13 | for _, logNPeers := range []uint{2, 3, 7, 10, 15, 17} { 14 | nPeers := (1 << logNPeers) - 1 15 | 16 | peers := make([]*Endpoint, nPeers) 17 | for i := range peers { 18 | peers[i] = NewEndpoint() 19 | } 20 | 21 | // Peer 0 initiates to the rest of the peers 22 | beforeSetup := time.Now() 23 | π0, sm := peers[0].SetupGroup(peers[1:]) 24 | afterSetup := time.Now() 25 | 26 | // Have peer 1 accept the setup message 27 | beforeProcessSetup := time.Now() 28 | π1 := peers[1].ProcessSetupMessage(sm[0]) 29 | afterProcessSetup := time.Now() 30 | 31 | // Have peer 0 update its key 32 | beforeUpdate := time.Now() 33 | um := π0.UpdateKey() 34 | afterUpdate := time.Now() 35 | 36 | // Have peer 1 accept the update message 37 | beforeProcessUpdate := time.Now() 38 | π1.ProcessUpdateMessage(um) 39 | afterProcessUpdate := time.Now() 40 | 41 | fmt.Printf("%7d %7d %7d %7d %7d\n", 42 | nPeers, 43 | afterSetup.Sub(beforeSetup)/time.Millisecond, 44 | afterProcessSetup.Sub(beforeProcessSetup)/time.Millisecond, 45 | afterUpdate.Sub(beforeUpdate)/time.Millisecond, 46 | afterProcessUpdate.Sub(beforeProcessUpdate)/time.Millisecond) 47 | } 48 | */ 49 | } 50 | 51 | func TestProtoMAC(t *testing.T) { 52 | key := []byte{0, 1, 2, 3} 53 | 54 | // TODO Populate some fields 55 | sm := &SetupMessage{} 56 | msm, err := NewMACMessage(key, sm) 57 | if err != nil { 58 | t.Fatalf("Setup MAC", err) 59 | } 60 | 61 | smsm, err := msm.ToSetupMessage() 62 | if err != nil { 63 | t.Fatalf("Setup Verify", err) 64 | } 65 | 66 | if !reflect.DeepEqual(sm, smsm) { 67 | t.Fatalf("Setup Mismatch [%+v] [%+v]", sm, smsm) 68 | } 69 | 70 | // TODO Populate some fields 71 | um := &UpdateMessage{} 72 | mum, err := NewMACMessage(key, um) 73 | if err != nil { 74 | t.Fatalf("Setup MAC", err) 75 | } 76 | 77 | umum, err := mum.ToUpdateMessage() 78 | if err != nil { 79 | t.Fatalf("Setup Verify", err) 80 | } 81 | 82 | if !reflect.DeepEqual(um, umum) { 83 | t.Fatalf("Setup Mismatch [%+v] [%+v]", um, umum) 84 | } 85 | } 86 | 87 | func TestProtoSetup(t *testing.T) { 88 | nPeers := 2 89 | 90 | peers := make([]*Endpoint, nPeers) 91 | for i := range peers { 92 | peers[i] = NewEndpoint() 93 | } 94 | 95 | // Peer 0 initiates to the rest of the peers 96 | π0, m := peers[0].SetupGroup(peers[1:]) 97 | 98 | // Verify that when each peer receives its setup message, it computes the 99 | // same tree key that the first peer did 100 | for i := range peers { 101 | if i == 0 { 102 | continue 103 | } 104 | 105 | π := peers[i].ProcessSetupMessage(m[i-1]) 106 | if π.tk != π0.tk { 107 | t.Fatalf("Tree key mismatch [%d]", i) 108 | } 109 | if π.sk != π0.sk { 110 | t.Fatalf("Stage key mismatch [%d]", i) 111 | } 112 | } 113 | } 114 | 115 | func TestProtoUpdate(t *testing.T) { 116 | nPeers := 15 117 | 118 | peers := make([]*Endpoint, nPeers) 119 | for i := range peers { 120 | peers[i] = NewEndpoint() 121 | } 122 | 123 | // Setup 124 | π := make([]*GroupState, nPeers) 125 | var sm []*MACMessage 126 | π[0], sm = peers[0].SetupGroup(peers[1:]) 127 | for i := range peers { 128 | if i == 0 { 129 | continue 130 | } 131 | 132 | π[i] = peers[i].ProcessSetupMessage(sm[i-1]) 133 | } 134 | 135 | // Have each endpoint update its key. At each step, verify that all peers 136 | // arrive at the same results 137 | for i := range peers { 138 | um := π[i].UpdateKey() 139 | 140 | for j := range peers { 141 | if j == i { 142 | continue 143 | } 144 | 145 | π[j].ProcessUpdateMessage(um) 146 | if π[j].tk != π[i].tk { 147 | t.Fatalf("Tree key mismatch [%d] [%d]", i, j) 148 | } 149 | if π[j].sk != π[i].sk { 150 | t.Fatalf("Stage key mismatch [%d] [%d]", i, j) 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | // Still need: 4 | // * DHKeyGen 5 | // * KeyExchangeKeyGen 6 | // * KeyExchange 7 | // * MAC 8 | 9 | // Tree Management functions 10 | 11 | type TreeNode struct { 12 | Left *TreeNode 13 | Right *TreeNode 14 | Value PrivateKey 15 | Size int 16 | } 17 | 18 | func (t TreeNode) IsLeaf() bool { 19 | return t.Left == nil && t.Right == nil 20 | } 21 | 22 | func CreateTree(λ []PrivateKey) *TreeNode { 23 | n := len(λ) 24 | if n == 1 { 25 | return &TreeNode{Left: nil, Right: nil, Value: λ[0], Size: 1} 26 | } 27 | 28 | m := pow2(n) 29 | L := CreateTree(λ[0:m]) 30 | R := CreateTree(λ[m:n]) 31 | 32 | k := ι(Exp(PK(L.Value), R.Value)) 33 | return &TreeNode{Left: L, Right: R, Value: k, Size: L.Size + R.Size} 34 | } 35 | 36 | func Copath(T *TreeNode, i int) []GroupElement { 37 | // XXX Stop condition not listed in paper 38 | if T.IsLeaf() { 39 | return []GroupElement{} 40 | } 41 | 42 | m := pow2(T.Size) 43 | 44 | var key GroupElement 45 | var remainder []GroupElement 46 | if i < m { 47 | key = PK(T.Right.Value) 48 | remainder = Copath(T.Left, i) 49 | } else { 50 | key = PK(T.Left.Value) 51 | remainder = Copath(T.Right, i-m) 52 | } 53 | 54 | return append([]GroupElement{key}, remainder...) 55 | } 56 | 57 | func PathNodeKeys(λ PrivateKey, P []GroupElement) []PrivateKey { 58 | nks := make([]PrivateKey, len(P)+1) 59 | nks[len(P)] = λ 60 | for n := len(P) - 1; n >= 0; n -= 1 { 61 | nks[n] = ι(Exp(P[n], nks[n+1])) 62 | } 63 | return nks 64 | } 65 | -------------------------------------------------------------------------------- /tree_test.go: -------------------------------------------------------------------------------- 1 | package treekeys 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | /* 8 | // Use for detailed debugging as needed 9 | func trunc(b [32]byte) []byte { 10 | return b[:4] 11 | } 12 | 13 | func printTree(t *TreeNode, depth int) { 14 | pad := "" 15 | for i := 0; i < depth; i += 1 { 16 | pad += " " 17 | } 18 | 19 | fmt.Printf("%sValue: priv=[%x] pub=[%x]\n", pad, trunc(t.Value), trunc(PK(t.Value))) 20 | 21 | if t.Left != nil { 22 | printTree(t.Left, depth+1) 23 | } 24 | 25 | if t.Right != nil { 26 | printTree(t.Right, depth+1) 27 | } 28 | } 29 | */ 30 | 31 | func TestTreeAndPath(t *testing.T) { 32 | maxPeers := 17 33 | 34 | for nPeers := 1; nPeers <= maxPeers; nPeers += 1 { 35 | λ := make([]PrivateKey, nPeers) 36 | for i := range λ { 37 | λ[i] = DHKeyGen() 38 | } 39 | 40 | tree := CreateTree(λ) 41 | if tree.Size != nPeers { 42 | t.Fatalf("Wrong tree size [%d] != [%d]", tree.Size, nPeers) 43 | } 44 | 45 | for i := range λ { 46 | P := Copath(tree, i) 47 | nks := PathNodeKeys(λ[i], P) 48 | if nks[0] != tree.Value { 49 | t.Fatalf("Tree key computation failed for node %d [%x] != [%x]", i) 50 | } 51 | } 52 | } 53 | } 54 | --------------------------------------------------------------------------------