func (m *accountManager) GenerateOTP(ctx scope.Context, heim *proto.Heim, kms security.KMS, account proto.Account) (*proto.OTP, error) { m.b.Lock() defer m.b.Unlock() if m.b.otps == nil { m.b.otps = map[snowflake.Snowflake]*proto.OTP{} } old, ok := m.b.otps[account.ID()] if ok && old.Validated { return nil, proto.ErrOTPAlreadyEnrolled } otp, err := heim.NewOTP(account) if err != nil { return nil, err } m.b.otps[account.ID()] = otp return otp, 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 }