package memberlist import ( "bytes" "fmt" "sync" ) type Keyring struct { // Keys stores the key data used during encryption and decryption. It is // ordered in such a way where the first key (index 0) is the primary key, // which is used for encrypting messages, and is the first key tried during // message decryption. keys [][]byte // The keyring lock is used while performing IO operations on the keyring. l sync.Mutex } // Init allocates substructures func (k *Keyring) init() { k.keys = make([][]byte, 0) } // NewKeyring constructs a new container for a set of encryption keys. The // keyring contains all key data used internally by memberlist. // // While creating a new keyring, you must do one of: // - Omit keys and primary key, effectively disabling encryption // - Pass a set of keys plus the primary key // - Pass only a primary key // // If only a primary key is passed, then it will be automatically added to the // keyring. If creating a keyring with multiple keys, one key must be designated // primary by passing it as the primaryKey. If the primaryKey does not exist in // the list of secondary keys, it will be automatically added at position 0. // // A key should be either 16, 24, or 32 bytes to select AES-128, // AES-192, or AES-256. func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) { keyring := &Keyring{} keyring.init() if len(keys) > 0 || len(primaryKey) > 0 { if len(primaryKey) == 0 { return nil, fmt.Errorf("Empty primary key not allowed") } if err := keyring.AddKey(primaryKey); err != nil { return nil, err } for _, key := range keys { if err := keyring.AddKey(key); err != nil { return nil, err } } } return keyring, nil } // ValidateKey will check to see if the key is valid and returns an error if not. // // key should be either 16, 24, or 32 bytes to select AES-128, // AES-192, or AES-256. func ValidateKey(key []byte) error { if l := len(key); l != 16 && l != 24 && l != 32 { return fmt.Errorf("key size must be 16, 24 or 32 bytes") } return nil } // AddKey will install a new key on the ring. Adding a key to the ring will make // it available for use in decryption. If the key already exists on the ring, // this function will just return noop. // // key should be either 16, 24, or 32 bytes to select AES-128, // AES-192, or AES-256. func (k *Keyring) AddKey(key []byte) error { if err := ValidateKey(key); err != nil { return err } // No-op if key is already installed for _, installedKey := range k.keys { if bytes.Equal(installedKey, key) { return nil } } keys := append(k.keys, key) primaryKey := k.GetPrimaryKey() if primaryKey == nil { primaryKey = key } k.installKeys(keys, primaryKey) return nil } // UseKey changes the key used to encrypt messages. This is the only key used to // encrypt messages, so peers should know this key before this method is called. func (k *Keyring) UseKey(key []byte) error { for _, installedKey := range k.keys { if bytes.Equal(key, installedKey) { k.installKeys(k.keys, key) return nil } } return fmt.Errorf("Requested key is not in the keyring") } // RemoveKey drops a key from the keyring. This will return an error if the key // requested for removal is currently at position 0 (primary key). func (k *Keyring) RemoveKey(key []byte) error { if bytes.Equal(key, k.keys[0]) { return fmt.Errorf("Removing the primary key is not allowed") } for i, installedKey := range k.keys { if bytes.Equal(key, installedKey) { keys := append(k.keys[:i], k.keys[i+1:]...) k.installKeys(keys, k.keys[0]) } } return nil } // installKeys will take out a lock on the keyring, and replace the keys with a // new set of keys. The key indicated by primaryKey will be installed as the new // primary key. func (k *Keyring) installKeys(keys [][]byte, primaryKey []byte) { k.l.Lock() defer k.l.Unlock() newKeys := [][]byte{primaryKey} for _, key := range keys { if !bytes.Equal(key, primaryKey) { newKeys = append(newKeys, key) } } k.keys = newKeys } // GetKeys returns the current set of keys on the ring. func (k *Keyring) GetKeys() [][]byte { k.l.Lock() defer k.l.Unlock() return k.keys } // GetPrimaryKey returns the key on the ring at position 0. This is the key used // for encrypting messages, and is the first key tried for decrypting messages. func (k *Keyring) GetPrimaryKey() (key []byte) { k.l.Lock() defer k.l.Unlock() if len(k.keys) > 0 { key = k.keys[0] } return }