func (pm *PM) transmitToAgent(kms security.KMS, client *Client) (*security.ManagedKey, error) { if client.UserID() != pm.Receiver { return nil, ErrAccessDenied } // Decrypt PM key pmKey := pm.EncryptedSystemKey.Clone() if err := kms.DecryptKey(&pmKey); err != nil { return nil, err } // Verify ReceiverMAC if err := pm.verifyKey(&pmKey); err != nil { return nil, err } // Encrypt PM key for agent encryptedPMKey := pmKey.Clone() encryptedPMKey.IV = pm.IV if err := encryptedPMKey.Encrypt(client.Authorization.ClientKey); err != nil { return nil, err } pm.EncryptedReceiverKey = &encryptedPMKey return &pmKey, nil }
func (sec *AccountSecurity) ResetPassword(kms security.KMS, password string) (*AccountSecurity, error) { kek := sec.SystemKey.Clone() if err := kms.DecryptKey(&kek); err != nil { return nil, fmt.Errorf("key decryption error: %s", err) } kek.IV = make([]byte, ClientKeyType.BlockSize()) copy(kek.IV, sec.Nonce) clientKey := security.KeyFromPasscode([]byte(password), sec.Nonce, sec.UserKey.KeyType) if err := kek.Encrypt(clientKey); err != nil { return nil, fmt.Errorf("key encryption error: %s", err) } var ( mac [16]byte key [32]byte ) copy(key[:], clientKey.Plaintext) poly1305.Sum(&mac, sec.Nonce, &key) nsec := &AccountSecurity{ Nonce: sec.Nonce, MAC: mac[:], SystemKey: sec.SystemKey, UserKey: kek, KeyPair: sec.KeyPair, } return nsec, nil }
func (gs *GrantManager) StaffGrantToAccount(ctx scope.Context, kms security.KMS, target Account) error { keyEncryptingKey := gs.KeyEncryptingKey.Clone() if err := kms.DecryptKey(&keyEncryptingKey); err != nil { return fmt.Errorf("key-encrypting-key decrypt error: %s", err) } subjectKeyPair := gs.SubjectKeyPair.Clone() if err := subjectKeyPair.Decrypt(&keyEncryptingKey); err != nil { return err } // TODO: customize public/private payloads kp := target.KeyPair() var payloadKey security.ManagedKey if gs.PayloadKey == nil { payloadKey = keyEncryptingKey } else { payloadKey = gs.PayloadKey.Clone() if err := kms.DecryptKey(&payloadKey); err != nil { return fmt.Errorf("payload-key decrypt error: %s", err) } } c, err := security.GrantPublicKeyCapability( kms, gs.SubjectNonce, &subjectKeyPair, &kp, nil, payloadKey.Plaintext) if err != nil { return err } return gs.Capabilities.Save(ctx, target, c) }
func (b *AccountManagerBinding) getOTP(db gorp.SqlExecutor, kms security.KMS, accountID snowflake.Snowflake) (*proto.OTP, error) { encryptedOTP, err := b.getRawOTP(db, accountID) if err != nil { return nil, err } key := security.ManagedKey{ KeyType: OTPKeyType, IV: encryptedOTP.IV, Ciphertext: encryptedOTP.EncryptedKey, ContextKey: "account", ContextValue: accountID.String(), } if err := kms.DecryptKey(&key); err != nil { return nil, err } uriBytes, err := security.DecryptGCM(&key, encryptedOTP.IV, encryptedOTP.Digest, encryptedOTP.EncryptedURI, nil) if err != nil { return nil, err } otp := &proto.OTP{ URI: string(uriBytes), Validated: encryptedOTP.Validated, } return otp, nil }
func NewPM(kms security.KMS, client *Client, initiatorNick string, receiver UserID, receiverNick string) ( *PM, *security.ManagedKey, error) { if client.Account == nil { return nil, nil, ErrAccessDenied } pmID, err := snowflake.New() if err != nil { return nil, nil, err } iv, err := kms.GenerateNonce(RoomMessageKeyType.BlockSize()) if err != nil { return nil, nil, err } encryptedSystemKey, err := kms.GenerateEncryptedKey(RoomMessageKeyType, "pm", pmID.String()) if err != nil { return nil, nil, err } pmKey := encryptedSystemKey.Clone() if err := kms.DecryptKey(&pmKey); err != nil { return nil, nil, fmt.Errorf("pm key decrypt: %s", err) } //pmKey.IV = iv userKey := client.Account.UserKey() if err := userKey.Decrypt(client.Authorization.ClientKey); err != nil { return nil, nil, fmt.Errorf("initiator account key decrypt: %s", err) } encryptedInitiatorKey := pmKey.Clone() encryptedInitiatorKey.IV = iv if err := encryptedInitiatorKey.Encrypt(&userKey); err != nil { return nil, nil, fmt.Errorf("initiator pm key encrypt: %s", err) } var ( mac [16]byte key [32]byte ) copy(key[:], pmKey.Plaintext) poly1305.Sum(&mac, []byte(receiver), &key) pm := &PM{ ID: pmID, Initiator: client.Account.ID(), InitiatorNick: initiatorNick, Receiver: receiver, ReceiverNick: receiverNick, ReceiverMAC: mac[:], IV: iv, EncryptedSystemKey: encryptedSystemKey, EncryptedInitiatorKey: &encryptedInitiatorKey, } return pm, &pmKey, nil }
func (r *roomManagerKey) StaffUnlock(kms security.KMS) (*security.ManagedKeyPair, error) { kek := r.RoomSecurity.KeyEncryptingKey.Clone() if err := kms.DecryptKey(&kek); err != nil { return nil, err } kp := r.KeyPair() if err := kp.Decrypt(&kek); err != nil { return nil, err } return &kp, nil }
func (pm *PM) transmitToAccount(kms security.KMS, pmKey *security.ManagedKey, receiver Account) (*PM, error) { userKey := receiver.SystemKey() if err := kms.DecryptKey(&userKey); err != nil { return nil, err } encryptedReceiverKey := pmKey.Clone() encryptedReceiverKey.IV = pm.IV if err := encryptedReceiverKey.Encrypt(&userKey); err != nil { return nil, err } pm.EncryptedReceiverKey = &encryptedReceiverKey return pm, nil }
func (pm *PM) upgradeToAccountReceiver(ctx scope.Context, b Backend, kms security.KMS, client *Client) (*security.ManagedKey, error) { // Verify that client and receiver agent share the same account. _, id := pm.Receiver.Parse() agent, err := b.AgentTracker().Get(ctx, id) if err != nil { return nil, err } if agent.AccountID != client.Account.ID().String() { return nil, ErrAccessDenied } // Unlock PM and verify Receiver. pmKey := pm.EncryptedSystemKey.Clone() if err := kms.DecryptKey(&pmKey); err != nil { return nil, err } if err := pm.verifyKey(&pmKey); err != nil { return nil, err } // Re-encrypt PM key for account. pm.Receiver = UserID(fmt.Sprintf("account:%s", client.Account.ID())) var ( mac [16]byte key [32]byte ) copy(key[:], pmKey.Plaintext) poly1305.Sum(&mac, []byte(pm.Receiver), &key) pm.ReceiverMAC = mac[:] userKey := client.Account.UserKey() if err := userKey.Decrypt(client.Authorization.ClientKey); err != nil { return nil, err } encryptedReceiverKey := pmKey.Clone() encryptedReceiverKey.IV = pm.IV if err := encryptedReceiverKey.Encrypt(&userKey); err != nil { return nil, err } pm.EncryptedReceiverKey = &encryptedReceiverKey if err := pm.verifyKey(&pmKey); err != nil { return nil, err } return &pmKey, nil }
func (rmkb *RoomManagerKeyBinding) StaffUnlock(kms security.KMS) (*security.ManagedKeyPair, error) { kek := security.ManagedKey{ KeyType: proto.RoomManagerKeyType, Ciphertext: rmkb.Room.EncryptedManagementKey, ContextKey: "room", ContextValue: rmkb.Room.Name, } if err := kms.DecryptKey(&kek); err != nil { return nil, err } kp := rmkb.KeyPair() if err := kp.Decrypt(&kek); err != nil { return nil, err } return &kp, nil }
func CheckEmailVerificationToken(kms security.KMS, account Account, email string, token []byte) error { systemKey := account.SystemKey() if err := kms.DecryptKey(&systemKey); err != nil { return err } expected, err := emailVerificationToken(&systemKey, email) if err != nil { return err } if !hmac.Equal(token, expected) { return ErrInvalidVerificationToken } return nil }
func NewRoomSecurity(kms security.KMS, roomName string) (*RoomSecurity, error) { kpType := security.Curve25519 // Use one KMS request to obtain all the randomness we need: // - key-encrypting-key IV // - private key for grants to accounts // - nonce for manager grants to accounts randomData, err := kms.GenerateNonce( RoomManagerKeyType.BlockSize() + kpType.PrivateKeySize() + kpType.NonceSize()) if err != nil { return nil, fmt.Errorf("rng error: %s", err) } randomReader := bytes.NewReader(randomData) // Generate IV with random data. iv := make([]byte, RoomManagerKeyType.BlockSize()) if _, err := io.ReadFull(randomReader, iv); err != nil { return nil, fmt.Errorf("rng error: %s", err) } // Generate private key using randomReader. keyPair, err := kpType.Generate(randomReader) if err != nil { return nil, fmt.Errorf("keypair generation error: %s", err) } // Generate nonce with random data. nonce := make([]byte, kpType.NonceSize()) if _, err := io.ReadFull(randomReader, nonce); err != nil { return nil, fmt.Errorf("rng error: %s", err) } // Generate key-encrypting-key. This will be returned encrypted, using the // name of the room as its context. encryptedKek, err := kms.GenerateEncryptedKey(RoomManagerKeyType, "room", roomName) if err != nil { return nil, fmt.Errorf("key generation error: %s", err) } // Decrypt key-encrypting-key so we can encrypt keypair. kek := encryptedKek.Clone() if err = kms.DecryptKey(&kek); err != nil { return nil, fmt.Errorf("key decryption error: %s", err) } // Encrypt private key. keyPair.IV = iv if err = keyPair.Encrypt(&kek); err != nil { return nil, fmt.Errorf("keypair encryption error: %s", err) } // Generate message authentication code, for verifying a given key-encryption-key. var ( mac [16]byte key [32]byte ) copy(key[:], kek.Plaintext) poly1305.Sum(&mac, iv, &key) sec := &RoomSecurity{ Nonce: nonce, MAC: mac[:], KeyEncryptingKey: *encryptedKek, KeyPair: *keyPair, } return sec, nil }
func NewRoom( ctx scope.Context, kms security.KMS, private bool, name, version string, managers ...proto.Account) ( proto.ManagedRoom, error) { sec, err := proto.NewRoomSecurity(kms, name) if err != nil { return nil, err } managerKey := sec.KeyEncryptingKey.Clone() if err := kms.DecryptKey(&managerKey); err != nil { return nil, err } roomKeyPair, err := sec.Unlock(&managerKey) if err != nil { return nil, err } room := &memRoom{ RoomBase: RoomBase{ name: name, version: version, log: newMemLog(), agentBans: map[proto.UserID]time.Time{}, ipBans: map[string]time.Time{}, }, sec: sec, managerKey: &roomManagerKey{ RoomSecurity: sec, GrantManager: &proto.GrantManager{ Capabilities: &capabilities{}, KeyEncryptingKey: &sec.KeyEncryptingKey, SubjectKeyPair: &sec.KeyPair, SubjectNonce: sec.Nonce, }, }, } room.managerKey.GrantManager.Managers = room.managerKey var ( roomMsgKey proto.RoomMessageKey msgKey security.ManagedKey ) if private { roomMsgKey, err = room.GenerateMessageKey(ctx, kms) if err != nil { return nil, err } msgKey = roomMsgKey.ManagedKey() if err := kms.DecryptKey(&msgKey); err != nil { return nil, err } } for _, manager := range managers { kp := manager.KeyPair() c, err := security.GrantPublicKeyCapability( kms, sec.Nonce, roomKeyPair, &kp, nil, managerKey.Plaintext) if err != nil { return nil, err } room.managerKey.Capabilities.Save(ctx, manager, c) if private { c, err = security.GrantPublicKeyCapability( kms, roomMsgKey.Nonce(), roomKeyPair, &kp, nil, msgKey.Plaintext) if err != nil { return nil, err } room.messageKey.Capabilities.Save(ctx, manager, c) } } return room, nil }
// NewAccountSecurity initializes the nonce and account secrets for a new account // with the given password. Returns an encrypted key-encrypting-key, encrypted // key-pair, nonce, and error. func NewAccountSecurity( kms security.KMS, password string) (*AccountSecurity, *security.ManagedKey, error) { kpType := security.Curve25519 // Use one KMS request to obtain all the randomness we need: // - nonce // - private key randomData, err := kms.GenerateNonce(kpType.NonceSize() + kpType.PrivateKeySize()) if err != nil { return nil, nil, fmt.Errorf("rng error: %s", err) } randomReader := bytes.NewReader(randomData) // Generate nonce with random data. Use to populate IV. nonce := make([]byte, kpType.NonceSize()) if _, err := io.ReadFull(randomReader, nonce); err != nil { return nil, nil, fmt.Errorf("rng error: %s", err) } iv := make([]byte, ClientKeyType.BlockSize()) copy(iv, nonce) // Generate key-encrypting-key using KMS. This will be returned encrypted, // using the base64 encoding of the nonce as its context. nonceBase64 := base64.URLEncoding.EncodeToString(nonce) systemKey, err := kms.GenerateEncryptedKey(ClientKeyType, "nonce", nonceBase64) if err != nil { return nil, nil, fmt.Errorf("key generation error: %s", err) } // Generate private key using randomReader. keyPair, err := kpType.Generate(randomReader) if err != nil { return nil, nil, fmt.Errorf("keypair generation error: %s", err) } // Decrypt key-encrypting-key so we can encrypt keypair, and so we can re-encrypt // it using the user's key. kek := systemKey.Clone() if err = kms.DecryptKey(&kek); err != nil { return nil, nil, fmt.Errorf("key decryption error: %s", err) } // Encrypt private key. keyPair.IV = iv if err = keyPair.Encrypt(&kek); err != nil { return nil, nil, fmt.Errorf("keypair encryption error: %s", err) } // Clone key-encrypting-key and encrypt with client key. clientKey := security.KeyFromPasscode([]byte(password), nonce, ClientKeyType) userKey := kek.Clone() userKey.IV = iv if err := userKey.Encrypt(clientKey); err != nil { return nil, nil, fmt.Errorf("key encryption error: %s", err) } // Generate message authentication code, for verifying passwords. var ( mac [16]byte key [32]byte ) copy(key[:], clientKey.Plaintext) poly1305.Sum(&mac, nonce, &key) sec := &AccountSecurity{ Nonce: nonce, MAC: mac[:], SystemKey: *systemKey, UserKey: userKey, KeyPair: *keyPair, } return sec, clientKey, nil }
func (b *Backend) CreateRoom( ctx scope.Context, kms security.KMS, private bool, name string, managers ...proto.Account) ( proto.ManagedRoom, error) { sec, err := proto.NewRoomSecurity(kms, name) if err != nil { return nil, err } logging.Logger(ctx).Printf("creating room: %s", name) room := &Room{ Name: name, IV: sec.KeyPair.IV, MAC: sec.MAC, Nonce: sec.Nonce, EncryptedManagementKey: sec.KeyEncryptingKey.Ciphertext, EncryptedPrivateKey: sec.KeyPair.EncryptedPrivateKey, PublicKey: sec.KeyPair.PublicKey, } var ( rmkb *RoomMessageKeyBinding msgKey security.ManagedKey ) if private { rmkb, err = room.generateMessageKey(b, kms) if err != nil { return nil, err } msgKey = rmkb.ManagedKey() if err := kms.DecryptKey(&msgKey); err != nil { return nil, err } } // Generate manager capabilities. managerKey := sec.KeyEncryptingKey.Clone() if err := kms.DecryptKey(&managerKey); err != nil { return nil, fmt.Errorf("manager key decrypt error: %s", err) } roomKeyPair, err := sec.Unlock(&managerKey) if err != nil { return nil, fmt.Errorf("room security unlock error: %s", err) } managerCaps := make([]*security.PublicKeyCapability, len(managers)) for i, manager := range managers { kp := manager.KeyPair() c, err := security.GrantPublicKeyCapability( kms, sec.Nonce, roomKeyPair, &kp, nil, managerKey.Plaintext) if err != nil { return nil, fmt.Errorf("manager grant error: %s", err) } managerCaps[i] = c } accessCaps := []*security.PublicKeyCapability{} if private { accessCaps = make([]*security.PublicKeyCapability, len(managers)) for i, manager := range managers { kp := manager.KeyPair() c, err := security.GrantPublicKeyCapability( kms, rmkb.Nonce(), roomKeyPair, &kp, nil, msgKey.Plaintext) if err != nil { return nil, fmt.Errorf("access grant error: %s", err) } accessCaps[i] = c } } // Insert data. t, err := b.DbMap.Begin() if err != nil { return nil, err } rollback := func() { if err := t.Rollback(); err != nil { logging.Logger(ctx).Printf("rollback error: %s", err) } } if err := t.Insert(room); err != nil { logging.Logger(ctx).Printf("room creation error on %s: %s", name, err) rollback() return nil, err } if rmkb != nil { if err := t.Insert(&rmkb.MessageKey, &rmkb.RoomMessageKey); err != nil { logging.Logger(ctx).Printf("room creation error on %s (message key): %s", name, err) rollback() return nil, err } } managerCapTable := RoomManagerCapabilities{ Room: room, Executor: t, } for i, capability := range managerCaps { if err := managerCapTable.Save(ctx, managers[i], capability); err != nil { logging.Logger(ctx).Printf( "room creation error on %s (manager %s): %s", name, managers[i].ID().String(), err) rollback() return nil, err } } messageCapTable := RoomMessageCapabilities{ Room: room, Executor: t, } for i, capability := range accessCaps { if err := messageCapTable.Save(ctx, managers[i], capability); err != nil { logging.Logger(ctx).Printf( "room creation error on %s (access capability): %s", name, err) rollback() return nil, err } } if err := t.Commit(); err != nil { logging.Logger(ctx).Printf("room creation error on %s (commit): %s", name, err) return nil, err } return room.Bind(b), nil }
func (b *AccountManagerBinding) GenerateOTP(ctx scope.Context, heim *proto.Heim, kms security.KMS, account proto.Account) (*proto.OTP, error) { encryptedKey, err := kms.GenerateEncryptedKey(OTPKeyType, "account", account.ID().String()) if err != nil { return nil, err } key := encryptedKey.Clone() if err := kms.DecryptKey(&key); err != nil { return nil, err } iv, err := kms.GenerateNonce(OTPKeyType.BlockSize()) if err != nil { return nil, err } t, err := b.DbMap.Begin() if err != nil { return nil, err } rawOTP, err := b.getRawOTP(t, account.ID()) if err != nil && err != proto.ErrOTPNotEnrolled { rollback(ctx, t) return nil, err } if err == nil { if rawOTP.Validated { rollback(ctx, t) return nil, proto.ErrOTPAlreadyEnrolled } row := &OTP{AccountID: account.ID().String()} if _, err := t.Delete(row); err != nil { rollback(ctx, t) return nil, err } } otp, err := heim.NewOTP(account) if err != nil { rollback(ctx, t) return nil, err } digest, encryptedURI, err := security.EncryptGCM(&key, iv, []byte(otp.URI), nil) if err != nil { rollback(ctx, t) return nil, err } row := &OTP{ AccountID: account.ID().String(), IV: iv, EncryptedKey: encryptedKey.Ciphertext, Digest: digest, EncryptedURI: encryptedURI, } if err := t.Insert(row); err != nil { // TODO: this could fail in the case of a race condition // by the time that matters we should be on postgres 9.5 and using a proper upsert rollback(ctx, t) return nil, err } if err := t.Commit(); err != nil { return nil, err } return otp, nil }