func (rb *RoomBinding) MessageKey(ctx scope.Context) (proto.RoomMessageKey, error) { var row struct { MessageKey RoomMessageKey } err := rb.DbMap.SelectOne( &row, "SELECT mk.id, mk.encrypted_key, mk.iv, mk.nonce,"+ " r.room, r.key_id, r.activated, r.expired, r.comment"+ " FROM master_key mk, room_master_key r"+ " WHERE r.room = $1 AND mk.id = r.key_id AND r.expired < r.activated"+ " ORDER BY r.activated DESC LIMIT 1", rb.Name) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } msgKey := &security.ManagedKey{ KeyType: proto.RoomMessageKeyType, IV: row.MessageKey.IV, Ciphertext: row.MessageKey.EncryptedKey, ContextKey: "room", ContextValue: rb.Room.Name, } var keyID snowflake.Snowflake if err := keyID.FromString(row.KeyID); err != nil { return nil, err } return NewRoomMessageKeyBinding(rb, keyID, msgKey, row.Nonce), nil }
func (c *Client) AuthenticateWithAgent(ctx scope.Context, backend Backend, agent *Agent, agentKey *security.ManagedKey) error { if agent.AccountID == "" { return nil } var accountID snowflake.Snowflake if err := accountID.FromString(agent.AccountID); err != nil { return err } account, err := backend.AccountManager().Get(ctx, accountID) if err != nil { if err == ErrAccountNotFound { return nil } return err } clientKey, err := agent.Unlock(agentKey) if err != nil { return fmt.Errorf("agent key error: %s", err) } c.Account = account c.Authorization.ClientKey = clientKey return nil }
func (s *Server) handleRoom(w http.ResponseWriter, r *http.Request) { ctx := s.rootCtx.Fork() // Resolve the room. // TODO: support room creation? roomName := mux.Vars(r)["room"] room, err := s.b.GetRoom(ctx, roomName) if err != nil { if err == proto.ErrRoomNotFound { http.Error(w, "404 page not found", http.StatusNotFound) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } // Tag the agent. We use an authenticated but un-encrypted cookie. agent, cookie, agentKey, err := getAgent(ctx, s, r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } client := &proto.Client{Agent: agent} client.FromRequest(ctx, r) // Look up account associated with agent. var accountID snowflake.Snowflake if err := accountID.FromString(agent.AccountID); agent.AccountID != "" && err == nil { if err := client.AuthenticateWithAgent(ctx, s.b, room, agent, agentKey); err != nil { fmt.Printf("agent auth failed: %s\n", err) switch err { case proto.ErrAccessDenied: http.Error(w, err.Error(), http.StatusForbidden) default: http.Error(w, err.Error(), http.StatusInternalServerError) } return } } // Upgrade to a websocket and set cookie. headers := http.Header{} if cookie != nil { headers.Add("Set-Cookie", cookie.String()) } conn, err := upgrader.Upgrade(w, r, headers) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // Serve the session. session := newSession(ctx, s, conn, roomName, room, client, agentKey) if err = session.serve(); err != nil { // TODO: error handling return } }
func getClient(ctx scope.Context, s *Server, r *http.Request) (*proto.Client, *http.Cookie, *security.ManagedKey, error) { agent, cookie, agentKey, err := getAgent(ctx, s, r) if err != nil { return nil, nil, nil, err } client := &proto.Client{ Agent: agent, Authorization: proto.Authorization{ ClientKey: agentKey, }, } client.FromRequest(ctx, r) // Look up account associated with agent. var accountID snowflake.Snowflake if err := accountID.FromString(agent.AccountID); agent.AccountID != "" && err == nil { if err := client.AuthenticateWithAgent(ctx, s.b, agent, agentKey); err != nil { fmt.Printf("agent auth failed: %s\n", err) switch err { case proto.ErrAccessDenied: // allow session to proceed, but agent will not be logged into account agent.AccountID = "" default: return nil, nil, nil, err } } } return client, cookie, agentKey, nil }
func (c *console) resolveAccount(ctx scope.Context, ref string) (proto.Account, error) { idx := strings.IndexRune(ref, ':') if idx < 0 { var accountID snowflake.Snowflake if err := accountID.FromString(ref); err != nil { return nil, err } return c.backend.AccountManager().Get(ctx, accountID) } return c.backend.AccountManager().Resolve(ctx, ref[:idx], ref[idx+1:]) }
func parseDeleteMessageArg(arg string) (string, snowflake.Snowflake, error) { parts := strings.SplitN(arg, ":", 2) if len(parts) != 2 { return "", 0, fmt.Errorf("format should be <room>:<message-id>") } var msgID snowflake.Snowflake if err := msgID.FromString(parts[1]); err != nil { return "", 0, err } return parts[0], msgID, nil }
func InitiatePM( ctx scope.Context, b Backend, kms security.KMS, client *Client, initiatorNick string, receiver UserID, receiverNick string) (*PM, error) { resolveAccount := func(accountIDStr string) (Account, error) { var accountID snowflake.Snowflake if err := accountID.FromString(accountIDStr); err != nil { return nil, err } return b.AccountManager().Get(ctx, accountID) } pm, pmKey, err := NewPM(kms, client, initiatorNick, receiver, receiverNick) if err != nil { return nil, fmt.Errorf("new pm: %s", err) } kind, id := receiver.Parse() switch kind { case "account": receiver, err := resolveAccount(id) if err != nil { return nil, err } return pm.transmitToAccount(kms, pmKey, receiver) case "agent", "bot": agent, err := b.AgentTracker().Get(ctx, id) if err != nil { return nil, err } if agent.AccountID != "" { receiver, err := resolveAccount(agent.AccountID) if err != nil { return nil, err } return pm.transmitToAccount(kms, pmKey, receiver) } // We can't transmit the key to the agent until the agent joins the chat. return pm, nil default: return nil, ErrInvalidUserID } }
func ParsePasswordResetConfirmation(confirmation string) (snowflake.Snowflake, []byte, error) { var id snowflake.Snowflake idx := strings.IndexRune(confirmation, '-') if idx < 0 { return id, nil, ErrInvalidConfirmationCode } mac, err := hex.DecodeString(confirmation[idx+1:]) if err != nil { return id, nil, ErrInvalidConfirmationCode } if err := id.FromString(confirmation[:idx]); err != nil { return id, nil, ErrInvalidConfirmationCode } return id, mac, nil }
func (rb *ManagedRoomBinding) MessageKey(ctx scope.Context) (proto.RoomMessageKey, error) { var row struct { MessageKey RoomMessageKey } mkCols, err := allColumns(rb.DbMap, row.MessageKey, "mk") if err != nil { return nil, err } rCols, err := allColumns(rb.DbMap, row.RoomMessageKey, "r") if err != nil { return nil, err } err = rb.DbMap.SelectOne( &row, fmt.Sprintf("SELECT %s, %s FROM master_key mk, room_master_key r"+ " WHERE r.room = $1 AND mk.id = r.key_id AND r.expired < r.activated"+ " ORDER BY r.activated DESC LIMIT 1", mkCols, rCols), rb.RoomName) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } msgKey := &security.ManagedKey{ KeyType: proto.RoomMessageKeyType, IV: row.MessageKey.IV, Ciphertext: row.MessageKey.EncryptedKey, ContextKey: "room", ContextValue: rb.RoomName, } var keyID snowflake.Snowflake if err := keyID.FromString(row.KeyID); err != nil { return nil, err } return NewRoomMessageKeyBinding(rb, keyID, msgKey, row.Nonce), nil }
func (b *AccountManagerBinding) resolve( db gorp.SqlExecutor, namespace, id string) (*AccountBinding, error) { var pid PersonalIdentity err := db.SelectOne( &pid, "SELECT account_id FROM personal_identity WHERE namespace = $1 AND id = $2", namespace, id) if err != nil { if err == sql.ErrNoRows { return nil, proto.ErrAccountNotFound } return nil, err } var accountID snowflake.Snowflake if err := accountID.FromString(pid.AccountID); err != nil { return nil, err } return b.get(db, accountID) }
func (s *Server) resolveRoom(ctx scope.Context, prefix, roomName string, client *proto.Client) (room proto.Room, err error) { // TODO: support room creation? switch prefix { case "pm:": var ( sf snowflake.Snowflake roomKey *security.ManagedKey ) if err := sf.FromString(roomName); err != nil { return nil, proto.ErrRoomNotFound } room, roomKey, err = s.b.PMTracker().Room(ctx, s.kms, sf, client) if err != nil { switch err { case proto.ErrAccessDenied, proto.ErrPMNotFound: return nil, proto.ErrRoomNotFound default: return nil, err } } client.Authorization.AddMessageKey("pm:"+roomName, roomKey) return room, nil case "": room, err = s.b.GetRoom(ctx, roomName) if s.allowRoomCreation && err == proto.ErrRoomNotFound { room, err = s.b.CreateRoom(ctx, s.kms, false, roomName) } if err != nil { return nil, err } if err := client.RoomAuthorize(ctx, room); err != nil { return nil, err } return room, nil default: return nil, proto.ErrRoomNotFound } }
func (ab *AccountBinding) ID() snowflake.Snowflake { var id snowflake.Snowflake _ = id.FromString(ab.Account.ID) return id }
func (c *Client) AuthenticateWithAgent( ctx scope.Context, backend Backend, room Room, agent *Agent, agentKey *security.ManagedKey) error { if agent.AccountID == "" { return nil } var accountID snowflake.Snowflake if err := accountID.FromString(agent.AccountID); err != nil { return err } account, err := backend.AccountManager().Get(ctx, accountID) if err != nil { if err == ErrAccountNotFound { return nil } return err } clientKey, err := agent.Unlock(agentKey) if err != nil { return fmt.Errorf("agent key error: %s", err) } c.Account = account c.Authorization.ClientKey = clientKey holderKey, err := account.Unlock(clientKey) if err != nil { return fmt.Errorf("client key error: %s", err) } managerKey, err := room.ManagerKey(ctx) if err != nil { return fmt.Errorf("manager key error: %s", err) } managerCap, err := room.ManagerCapability(ctx, account) if err != nil && err != ErrManagerNotFound { return err } if err == nil { subjectKey := managerKey.KeyPair() pc := &security.PublicKeyCapability{Capability: managerCap} secretJSON, err := pc.DecryptPayload(&subjectKey, holderKey) if err != nil { return fmt.Errorf("manager capability decrypt error: %s", err) } c.Authorization.ManagerKeyEncryptingKey = &security.ManagedKey{ KeyType: RoomManagerKeyType, } err = json.Unmarshal(secretJSON, &c.Authorization.ManagerKeyEncryptingKey.Plaintext) if err != nil { return fmt.Errorf("manager key unmarshal error: %s", err) } managerKeyPair, err := managerKey.Unlock(c.Authorization.ManagerKeyEncryptingKey) if err != nil { return fmt.Errorf("manager key unlock error: %s", err) } c.Authorization.ManagerKeyPair = managerKeyPair } // Look for message key grants to this account. messageKey, err := room.MessageKey(ctx) if err != nil { return err } if messageKey != nil { capability, err := messageKey.AccountCapability(ctx, account) if err != nil { return fmt.Errorf("access capability error: %s", err) } if capability != nil { subjectKey := managerKey.KeyPair() roomKeyJSON, err := capability.DecryptPayload(&subjectKey, holderKey) if err != nil { return fmt.Errorf("access capability decrypt error: %s", err) } roomKey := &security.ManagedKey{ KeyType: security.AES128, } if err := json.Unmarshal(roomKeyJSON, &roomKey.Plaintext); err != nil { return fmt.Errorf("access capability unmarshal error: %s", err) } c.Authorization.AddMessageKey(messageKey.KeyID(), roomKey) c.Authorization.CurrentMessageKeyID = messageKey.KeyID() } } return nil }
func (t *PMTracker) Initiate( ctx scope.Context, kms security.KMS, room proto.Room, client *proto.Client, recipient proto.UserID) ( snowflake.Snowflake, error) { initiatorNick, ok, err := room.ResolveNick(ctx, proto.UserID(fmt.Sprintf("account:%s", client.Account.ID()))) if err != nil { return 0, err } if !ok { initiatorNick = fmt.Sprintf("account:%s", client.Account.ID()) } recipientNick, ok, err := room.ResolveNick(ctx, recipient) if err != nil { return 0, err } if !ok { recipientNick = string(recipient) } pm, err := proto.InitiatePM(ctx, t.Backend, kms, client, initiatorNick, recipient, recipientNick) if err != nil { return 0, err } row := &PM{ ID: pm.ID.String(), Initiator: pm.Initiator.String(), InitiatorNick: pm.InitiatorNick, Receiver: string(pm.Receiver), ReceiverNick: pm.ReceiverNick, ReceiverMAC: pm.ReceiverMAC, IV: pm.IV, EncryptedSystemKey: pm.EncryptedSystemKey.Ciphertext, EncryptedInitiatorKey: pm.EncryptedInitiatorKey.Ciphertext, } if pm.EncryptedReceiverKey != nil { row.EncryptedReceiverKey = pm.EncryptedReceiverKey.Ciphertext } // Look for existing PM to reuse. tx, err := t.DbMap.Begin() if err != nil { return 0, err } var existingRow PM err = tx.SelectOne( &existingRow, "SELECT id FROM pm WHERE initiator = $1 AND receiver = $2", client.Account.ID().String(), string(recipient)) if err != nil && err != sql.ErrNoRows { rollback(ctx, tx) return 0, err } if err == nil { rollback(ctx, tx) var pmID snowflake.Snowflake if err := pmID.FromString(existingRow.ID); err != nil { return 0, err } return pmID, nil } kind, id := recipient.Parse() if kind == "account" { var existingRow PM err = tx.SelectOne( &existingRow, "SELECT id FROM pm WHERE initiator = $1 AND receiver = $2", id, string(client.UserID())) if err != nil && err != sql.ErrNoRows { rollback(ctx, tx) return 0, err } if err == nil { rollback(ctx, tx) var pmID snowflake.Snowflake if err := pmID.FromString(existingRow.ID); err != nil { return 0, err } return pmID, nil } } if err := tx.Insert(row); err != nil { rollback(ctx, tx) return 0, err } if err := tx.Commit(); err != nil { return 0, err } return pm.ID, nil }